Commit e5901cad by Ollie Wild Committed by Ollie Wild

fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.

	gcc/
	fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.
	(get_pointer_modulus_and_residue): New function.

	gcc/testsuite/
	gcc.dg/fold-bitand-1.c: New test.
	gcc.dg/fold-bitand-2.c: New test.
	gcc.dg/fold-bitand-3.c: New test.
	gcc.dg/fold-bitand-4.c: New test.

From-SVN: r128701
parent 50b73fcd
2007-09-23 Ollie Wild <aaw@google.com>
fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.
(get_pointer_modulus_and_residue): New function.
2007-09-23 Richard Sandiford <rsandifo@nildram.co.uk> 2007-09-23 Richard Sandiford <rsandifo@nildram.co.uk>
* config/mips/mips.c (build_mips16_call_stub): On 64-bit targets, * config/mips/mips.c (build_mips16_call_stub): On 64-bit targets,
...@@ -9384,6 +9384,97 @@ fold_mult_zconjz (tree type, tree expr) ...@@ -9384,6 +9384,97 @@ fold_mult_zconjz (tree type, tree expr)
} }
/* Subroutine of fold_binary. If P is the value of EXPR, computes
power-of-two M and (arbitrary) N such that M divides (P-N). This condition
guarantees that P and N have the same least significant log2(M) bits.
N is not otherwise constrained. In particular, N is not normalized to
0 <= N < M as is common. In general, the precise value of P is unknown.
M is chosen as large as possible such that constant N can be determined.
Returns M and sets *RESIDUE to N. */
static unsigned HOST_WIDE_INT
get_pointer_modulus_and_residue (tree expr, unsigned HOST_WIDE_INT *residue)
{
enum tree_code code;
*residue = 0;
code = TREE_CODE (expr);
if (code == ADDR_EXPR)
{
expr = TREE_OPERAND (expr, 0);
if (handled_component_p (expr))
{
HOST_WIDE_INT bitsize, bitpos;
tree offset;
enum machine_mode mode;
int unsignedp, volatilep;
expr = get_inner_reference (expr, &bitsize, &bitpos, &offset,
&mode, &unsignedp, &volatilep, false);
*residue = bitpos / BITS_PER_UNIT;
if (offset)
{
if (TREE_CODE (offset) == INTEGER_CST)
*residue += TREE_INT_CST_LOW (offset);
else
/* We don't handle more complicated offset expressions. */
return 1;
}
}
if (DECL_P (expr))
return DECL_ALIGN_UNIT (expr);
}
else if (code == POINTER_PLUS_EXPR)
{
tree op0, op1;
unsigned HOST_WIDE_INT modulus;
enum tree_code inner_code;
op0 = TREE_OPERAND (expr, 0);
STRIP_NOPS (op0);
modulus = get_pointer_modulus_and_residue (op0, residue);
op1 = TREE_OPERAND (expr, 1);
STRIP_NOPS (op1);
inner_code = TREE_CODE (op1);
if (inner_code == INTEGER_CST)
{
*residue += TREE_INT_CST_LOW (op1);
return modulus;
}
else if (inner_code == MULT_EXPR)
{
op1 = TREE_OPERAND (op1, 1);
if (TREE_CODE (op1) == INTEGER_CST)
{
unsigned HOST_WIDE_INT align;
/* Compute the greatest power-of-2 divisor of op1. */
align = TREE_INT_CST_LOW (op1);
align &= -align;
/* If align is non-zero and less than *modulus, replace
*modulus with align., If align is 0, then either op1 is 0
or the greatest power-of-2 divisor of op1 doesn't fit in an
unsigned HOST_WIDE_INT. In either case, no additional
constraint is imposed. */
if (align)
modulus = MIN (modulus, align);
return modulus;
}
}
}
/* If we get here, we were unable to determine anything useful about the
expression. */
return 1;
}
/* Fold a binary expression of code CODE and type TYPE with operands /* Fold a binary expression of code CODE and type TYPE with operands
OP0 and OP1. Return the folded expression if folding is OP0 and OP1. Return the folded expression if folding is
successful. Otherwise, return NULL_TREE. */ successful. Otherwise, return NULL_TREE. */
...@@ -10915,6 +11006,23 @@ fold_binary (enum tree_code code, tree type, tree op0, tree op1) ...@@ -10915,6 +11006,23 @@ fold_binary (enum tree_code code, tree type, tree op0, tree op1)
TREE_OPERAND (arg1, 0))); TREE_OPERAND (arg1, 0)));
} }
/* If arg0 is derived from the address of an object or function, we may
be able to fold this expression using the object or function's
alignment. */
if (POINTER_TYPE_P (TREE_TYPE (arg0)) && host_integerp (arg1, 1))
{
unsigned HOST_WIDE_INT modulus, residue;
unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (arg1);
modulus = get_pointer_modulus_and_residue (arg0, &residue);
/* This works because modulus is a power of 2. If this weren't the
case, we'd have to replace it by its greatest power-of-2
divisor: modulus & -modulus. */
if (low < modulus)
return build_int_cst (type, residue & low);
}
goto associate; goto associate;
case RDIV_EXPR: case RDIV_EXPR:
......
2007-09-23 Ollie Wild <aaw@google.com>
gcc.dg/fold-bitand-1.c: New test.
gcc.dg/fold-bitand-2.c: New test.
gcc.dg/fold-bitand-3.c: New test.
gcc.dg/fold-bitand-4.c: New test.
2007-09-23 Richard Sandiford <rsandifo@nildram.co.uk> 2007-09-23 Richard Sandiford <rsandifo@nildram.co.uk>
* gcc.target/mips/gcc-have-sync-compare-and-swap-1.c: Don't expect * gcc.target/mips/gcc-have-sync-compare-and-swap-1.c: Don't expect
/* { dg-do compile } */
/* { dg-options "-fdump-tree-original" } */
char c1 __attribute__ ((aligned (1)));
char c2 __attribute__ ((aligned (2)));
char c4 __attribute__ ((aligned (4)));
char c8 __attribute__ ((aligned (8)));
unsigned f1(void)
{
return 3 & (__SIZE_TYPE__)&c1;
}
unsigned f2(void)
{
return 3 & (__SIZE_TYPE__)&c2;
}
unsigned f3(void)
{
return 3 & (__SIZE_TYPE__)&c4;
}
unsigned f4(void)
{
return 3 & (__SIZE_TYPE__)&c8;
}
/* { dg-final { scan-tree-dump-times "\&c1 \& 3" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "\&c2 \& 3" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "\&c4 \& 3" 0 "original" } } */
/* { dg-final { scan-tree-dump-times "\&c8 \& 3" 0 "original" } } */
/* { dg-final { scan-tree-dump-times "return 0" 2 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */
/* { dg-do compile } */
/* { dg-options "-fdump-tree-original" } */
struct {
char c1;
char c2;
char c3;
char c4;
} s __attribute__ ((aligned (4)));
unsigned f1 (void)
{
return 3 & (__SIZE_TYPE__)&s.c1;
}
unsigned f2 (void)
{
return 3 & (__SIZE_TYPE__)&s.c2;
}
unsigned f3 (void)
{
return 3 & (__SIZE_TYPE__)&s.c3;
}
unsigned f4 (void)
{
return 3 & (__SIZE_TYPE__)&s.c4;
}
unsigned f5 (void)
{
return 4 & (__SIZE_TYPE__)&s.c1;
}
/* { dg-final { scan-tree-dump-times "\& 3" 0 "original" } } */
/* { dg-final { scan-tree-dump-times "\& 4" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "return 0" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "return 1" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "return 2" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "return 3" 1 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */
/* { dg-do compile } */
/* { dg-options "-fdump-tree-original" } */
char c[4] __attribute__ ((aligned (4)));
struct S {
char c1;
char c2;
char c3;
char c4;
};
int f1 (void)
{
return 3 & (__SIZE_TYPE__)&c[1];
}
int f2 (void)
{
return 3 & (__SIZE_TYPE__)&((struct S *)&c)->c2;
}
/* { dg-final { scan-tree-dump-times "\& 3" 0 "original" } } */
/* { dg-final { scan-tree-dump-times "return 1" 2 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */
/* { dg-do compile } */
/* { dg-options "-fdump-tree-original" } */
typedef char char4[4] __attribute__ ((aligned (4)));
char4 c4[4] __attribute__ ((aligned (16)));
typedef char char16[16] __attribute__ ((aligned (16)));
char16 c16[4] __attribute__ ((aligned (4)));
int f1 (void)
{
/* 12 */
return 15 & (__SIZE_TYPE__)&c4[3];
}
int f2 (int i)
{
/* Indeterminate */
return 15 & (__SIZE_TYPE__)&c4[i];
}
int f3 (int i)
{
/* 0 */
return 3 & (__SIZE_TYPE__)&c4[i];
}
int f4 (int i)
{
/* Indeterminate */
return 7 & (__SIZE_TYPE__)&c16[i];
}
int f5 (int i)
{
/* 0 */
return 3 & (__SIZE_TYPE__)&c16[i];
}
/* { dg-final { scan-tree-dump-times "return 12" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "\& 15" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "return 0" 2 "original" } } */
/* { dg-final { scan-tree-dump-times "\& 7" 1 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment