Thank you for the feedback. I have made the minor changes that were requested.
Additionally, I extracted the repetitive code into a reusable helper function,
match_plus_neg_pattern, making the code much more readable. Furthermore, the
logic, code, and tests remain the same as in version 2 of the patch.
gcc/ChangeLog:
* match.pd: New pattern.
* simplify-rtx.cc (match_plus_neg_pattern): New helper function.
(simplify_context::simplify_binary_operation_1): New
code to handle (a - 1) & -a, (a - 1) | -a and (a - 1) ^ -a.
gcc/testsuite/ChangeLog:
* gcc.dg/tree-ssa/bitops-11.c: New test.
(bit_and:c @0 (bit_not (bit_xor:c @0 @1)))
(bit_and @0 @1))
+/* Transform:
+ (a - 1) & -a -> 0.
+ (a - 1) | -a -> -1.
+ (a - 1) ^ -a -> -1. */
+(for bit_op (bit_ior bit_xor bit_and)
+ (simplify
+ (bit_op:c (plus @0 integer_minus_onep) (negate @0))
+ (if (bit_op == BIT_AND_EXPR)
+ { build_zero_cst (type); }
+ { build_minus_one_cst (type); }))
+ (simplify
+ (bit_op:c (minus @0 integer_onep) (negate @0))
+ (if (bit_op == BIT_AND_EXPR)
+ { build_zero_cst (type); }
+ { build_minus_one_cst (type); })))
+
/* a & (a == b) --> a & b (boolean version of the above). */
(simplify
(bit_and:c @0 (nop_convert? (eq:c @0 @1)))
return NULL_RTX;
}
+/* Returns true if OP0 and OP1 match the pattern (OP (plus (A - 1)) (neg A)),
+ and the pattern can be simplified (there are no side effects). */
+
+static bool
+match_plus_neg_pattern (rtx op0, rtx op1, machine_mode mode)
+{
+ /* Remove SUBREG from OP0 and OP1, if needed. */
+ if (GET_CODE (op0) == SUBREG
+ && GET_CODE (op1) == SUBREG
+ && subreg_lowpart_p (op0)
+ && subreg_lowpart_p (op1))
+ {
+ op0 = XEXP (op0, 0);
+ op1 = XEXP (op1, 0);
+ }
+
+ /* Check for the pattern (OP (plus (A - 1)) (neg A)). */
+ if (((GET_CODE (op1) == NEG
+ && GET_CODE (op0) == PLUS
+ && XEXP (op0, 1) == CONSTM1_RTX (mode))
+ || (GET_CODE (op0) == NEG
+ && GET_CODE (op1) == PLUS
+ && XEXP (op1, 1) == CONSTM1_RTX (mode)))
+ && rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0))
+ && !side_effects_p (XEXP (op0, 0)))
+ return true;
+ return false;
+}
+
/* Subroutine of simplify_binary_operation. Simplify a binary operation
CODE with result mode MODE, operating on OP0 and OP1. If OP0 and/or
OP1 are constant pool references, TRUEOP0 and TRUEOP1 represent the
&& GET_MODE_CLASS (mode) != MODE_CC)
return CONSTM1_RTX (mode);
+ /* Convert (ior (plus (A - 1)) (neg A)) to -1. */
+ if (match_plus_neg_pattern (op0, op1, mode))
+ return CONSTM1_RTX (mode);
+
/* (ior A C) is C if all bits of A that might be nonzero are on in C. */
if (CONST_INT_P (op1)
&& HWI_COMPUTABLE_MODE_P (mode)
& nonzero_bits (op1, mode)) == 0)
return (simplify_gen_binary (IOR, mode, op0, op1));
+ /* Convert (xor (plus (A - 1)) (neg A)) to -1. */
+ if (match_plus_neg_pattern (op0, op1, mode))
+ return CONSTM1_RTX (mode);
+
/* Convert (XOR (NOT x) (NOT y)) to (XOR x y).
Also convert (XOR (NOT x) y) to (NOT (XOR x y)), similarly for
(NOT y). */
&& GET_MODE_CLASS (mode) != MODE_CC)
return CONST0_RTX (mode);
+ /* Convert (and (plus (A - 1)) (neg A)) to 0. */
+ if (match_plus_neg_pattern (op0, op1, mode))
+ return CONST0_RTX (mode);
+
/* Transform (and (extend X) C) into (zero_extend (and X C)) if
there are no nonzero bits of C outside of X's mode. */
if ((GET_CODE (op0) == SIGN_EXTEND
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized-raw -Wno-psabi" } */
+
+typedef int v4si __attribute__((vector_size(4 * sizeof(int))));
+
+/* Generic */
+#define BIT_OPERATIONS(result_type, operand_type, suffix) \
+result_type foo_generic_1##suffix(operand_type a) { \
+ return (a - 1) & -a; \
+} \
+result_type foo_generic_2##suffix(operand_type a) { \
+ return (a - 1) | -a; \
+} \
+result_type foo_generic_3##suffix(operand_type a) { \
+ return (a - 1) ^ -a; \
+}
+
+BIT_OPERATIONS(signed char, signed char, 11)
+BIT_OPERATIONS(unsigned char, unsigned char, 12)
+
+BIT_OPERATIONS(signed short, signed short, 21)
+BIT_OPERATIONS(unsigned short, unsigned short, 22)
+
+BIT_OPERATIONS(signed int, signed int, 31)
+BIT_OPERATIONS(unsigned int, unsigned int, 32)
+
+BIT_OPERATIONS(signed long, signed long, 41)
+BIT_OPERATIONS(unsigned long, unsigned long, 42)
+
+/* Gimple */
+int
+foo_gimple_1(int a)
+{
+ int t1 = a - 1;
+ int t2 = -a;
+ int t3 = t1 & t2;
+ return t3;
+}
+
+short
+foo_gimple_2(short a)
+{
+ short t1 = a - 1;
+ short t2 = -a;
+ short t3 = t1 | t2;
+ return t3;
+}
+
+unsigned long
+foo_gimple_3(unsigned long a)
+{
+ unsigned long t1 = a - 1;
+ unsigned long t2 = -a;
+ unsigned long t3 = t1 ^ t2;
+ return t3;
+}
+
+int
+foo_gimple_4(int a, unsigned char b)
+{
+ /* The return expression should simplify to b + 7. */
+ int t1 = b;
+ t1 |= (a - 1) | -a;
+ t1 |= b & (a >> 3);
+
+ int t2 = b + 7;
+ t2 &= ~((b - 1) & -b);
+ t2 &= (a - 1) ^ -a;
+
+ int t3 = t1 & t2;
+ return t3;
+}
+
+/* Vectors */
+v4si
+foo_vector_1(v4si a)
+{
+ return (a - (v4si) {1, 1, 1, 1}) & -a;
+}
+
+v4si
+foo_vector_2(v4si a)
+{
+ v4si t0 = (v4si) {1, 1, 1, 1};
+ v4si t1 = a - t0;
+ v4si t2 = -a;
+ v4si t3 = t1 | t2;
+ return t3;
+}
+
+v4si
+foo_vector_3(v4si a)
+{
+ v4si t0 = (v4si) {1, 1, 1, 1};
+ v4si t1 = a - t0;
+ v4si t2 = -a;
+ v4si t3 = t1 ^ t2;
+ return t3;
+}
+
+v4si
+foo_vector_4(v4si a)
+{
+ v4si t0 = (v4si) {1, 1, 1, 1};
+ v4si t1 = (a - t0) & -a;
+ v4si t2 = (a - t0) | -a;
+ v4si t3 = (a - t0) ^ -a;
+ v4si t4 = t1 - t2 + t3;
+ return t4;
+}
+
+/* { dg-final { scan-tree-dump-not "bit_and_expr, " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "bit_ior_expr, " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "bit_xor_expr, " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "negate_expr, " "optimized" } } */
+/* { dg-final { scan-tree-dump-times "plus_expr, " 1 "optimized" } } */
+