From 1acdf11bd5d3eb94bf6e1fdefe5d3705cd0b3741 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Fri, 27 May 2005 02:46:01 +0000 Subject: [PATCH] re PR tree-optimization/9814 (gcc fails to optimise if (l&2) l|=2 away) PR tree-optimization/9814 * ifcvt.c (noce_emit_move_insn): If we fail to recognize the move instruction, add the necessary clobbers by re-expanding the RTL for arithmetic operations via optab.c's expand_unop/expand_binop. (noce_try_bitop): New function to optimize bit manipulation idioms of the form "if (x & C) x = x op C" and "if (!(x & C) x = x op C". (noce_process_if_block): Call noce_try_bitop. * gcc.dg/pr9814-1.c: New test case. From-SVN: r100240 --- gcc/ChangeLog | 10 +++ gcc/ifcvt.c | 153 +++++++++++++++++++++++++++++++- gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/gcc.dg/pr9814-1.c | 107 ++++++++++++++++++++++ 4 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/pr9814-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 433b0f0b5f3..190a25ad6ce 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2005-05-26 Roger Sayle + + PR tree-optimization/9814 + * ifcvt.c (noce_emit_move_insn): If we fail to recognize the move + instruction, add the necessary clobbers by re-expanding the RTL + for arithmetic operations via optab.c's expand_unop/expand_binop. + (noce_try_bitop): New function to optimize bit manipulation idioms + of the form "if (x & C) x = x op C" and "if (!(x & C) x = x op C". + (noce_process_if_block): Call noce_try_bitop. + 2005-05-26 Roger Sayle * reg-stack.c (convert_regs_entry, convert_regs_exit, diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index be02aaa7d24..9575e62b2f1 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -687,7 +687,57 @@ noce_emit_move_insn (rtx x, rtx y) if (GET_CODE (x) != STRICT_LOW_PART) { - emit_move_insn (x, y); + rtx seq, insn, target; + optab ot; + + start_sequence (); + insn = emit_move_insn (x, y); + seq = get_insns (); + end_sequence(); + + if (recog_memoized (insn) <= 0) + switch (GET_RTX_CLASS (GET_CODE (y))) + { + case RTX_UNARY: + ot = code_to_optab[GET_CODE (y)]; + if (ot) + { + start_sequence (); + target = expand_unop (GET_MODE (y), ot, XEXP (y, 0), x, 0); + if (target != NULL_RTX) + { + if (target != x) + emit_move_insn (x, target); + seq = get_insns (); + } + end_sequence (); + } + break; + + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: + ot = code_to_optab[GET_CODE (y)]; + if (ot) + { + start_sequence (); + target = expand_binop (GET_MODE (y), ot, + XEXP (y, 0), XEXP (y, 1), + x, 0, OPTAB_DIRECT); + if (target != NULL_RTX) + { + if (target != x) + emit_move_insn (x, target); + seq = get_insns (); + } + end_sequence (); + } + break; + + default: + break; + } + + emit_insn (seq); return; } @@ -1815,6 +1865,105 @@ noce_try_sign_mask (struct noce_if_info *if_info) } +/* Optimize away "if (x & C) x |= C" and similar bit manipulation + transformations. */ + +static int +noce_try_bitop (struct noce_if_info *if_info) +{ + rtx cond, x, a, result, seq; + enum machine_mode mode; + enum rtx_code code; + int bitnum; + + x = if_info->x; + cond = if_info->cond; + code = GET_CODE (cond); + + /* Check for no else condition. */ + if (! rtx_equal_p (x, if_info->b)) + return FALSE; + + /* Check for a suitable condition. */ + if (code != NE && code != EQ) + return FALSE; + if (XEXP (cond, 1) != const0_rtx) + return FALSE; + cond = XEXP (cond, 0); + + /* ??? We could also handle AND here. */ + if (GET_CODE (cond) == ZERO_EXTRACT) + { + if (XEXP (cond, 1) != const1_rtx + || GET_CODE (XEXP (cond, 2)) != CONST_INT + || ! rtx_equal_p (x, XEXP (cond, 0))) + return FALSE; + bitnum = INTVAL (XEXP (cond, 2)); + mode = GET_MODE (x); + if (bitnum >= HOST_BITS_PER_WIDE_INT) + return FALSE; + } + else + return FALSE; + + a = if_info->a; + if (GET_CODE (a) == IOR || GET_CODE (a) == XOR) + { + /* Check for "if (X & C) x = x op C". */ + if (! rtx_equal_p (x, XEXP (a, 0)) + || GET_CODE (XEXP (a, 1)) != CONST_INT + || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode)) + != (unsigned HOST_WIDE_INT) 1 << bitnum) + return FALSE; + + /* if ((x & C) == 0) x |= C; is transformed to x |= C. */ + /* if ((x & C) != 0) x |= C; is transformed to nothing. */ + if (GET_CODE (a) == IOR) + result = (code == NE) ? a : NULL_RTX; + else if (code == NE) + { + /* if ((x & C) == 0) x ^= C; is transformed to x |= C. */ + result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode); + result = simplify_gen_binary (IOR, mode, x, result); + } + else + { + /* if ((x & C) != 0) x ^= C; is transformed to x &= ~C. */ + result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode); + result = simplify_gen_binary (AND, mode, x, result); + } + } + else if (GET_CODE (a) == AND) + { + /* Check for "if (X & C) x &= ~C". */ + if (! rtx_equal_p (x, XEXP (a, 0)) + || GET_CODE (XEXP (a, 1)) != CONST_INT + || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode)) + != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode))) + return FALSE; + + /* if ((x & C) == 0) x &= ~C; is transformed to nothing. */ + /* if ((x & C) != 0) x &= ~C; is transformed to x &= ~C. */ + result = (code == EQ) ? a : NULL_RTX; + } + else + return FALSE; + + if (result) + { + start_sequence (); + noce_emit_move_insn (x, result); + seq = end_ifcvt_sequence (if_info); + if (!seq) + return FALSE; + + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATOR (if_info->insn_a)); + } + return TRUE; +} + + /* Similar to get_condition, only the resulting condition must be valid at JUMP, instead of at EARLIEST. */ @@ -2078,6 +2227,8 @@ noce_process_if_block (struct ce_if_block * ce_info) goto success; if (noce_try_store_flag (&if_info)) goto success; + if (noce_try_bitop (&if_info)) + goto success; if (noce_try_minmax (&if_info)) goto success; if (noce_try_abs (&if_info)) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d460ab34dde..00185e8011a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2005-05-26 Roger Sayle + + PR tree-optimization/9814 + * gcc.dg/pr9814-1.c: New test case. + 2005-05-26 Ziemowit Laski * objc.dg/comp-types-8.m, objc.dg/encode-6.m, diff --git a/gcc/testsuite/gcc.dg/pr9814-1.c b/gcc/testsuite/gcc.dg/pr9814-1.c new file mode 100644 index 00000000000..51b79f7839b --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr9814-1.c @@ -0,0 +1,107 @@ +/* PR tree-optimization/9814 */ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +extern void abort(void); + +int test1(int x) +{ + if (x & 2) + x |= 2; + return x; +} + +int test2(int x) +{ + if (!(x & 2)) + x |= 2; + return x; +} + +int test3(int x) +{ + if (x & 2) + x ^= 2; + return x; +} + +int test4(int x) +{ + if (!(x & 2)) + x ^= 2; + return x; +} + +int test5(int x) +{ + if (x & 2) + x &= ~2; + return x; +} + +int test6(int x) +{ + if (!(x & 2)) + x &= ~2; + return x; +} + +int main() +{ + if (test1(0) != 0) + abort(); + if (test1(2) != 2) + abort(); + if (test1(5) != 5) + abort(); + if (test1(7) != 7) + abort(); + + if (test2(0) != 2) + abort(); + if (test2(2) != 2) + abort(); + if (test2(5) != 7) + abort(); + if (test2(7) != 7) + abort(); + + if (test3(0) != 0) + abort(); + if (test3(2) != 0) + abort(); + if (test3(5) != 5) + abort(); + if (test3(7) != 5) + abort(); + + if (test4(0) != 2) + abort(); + if (test4(2) != 2) + abort(); + if (test4(5) != 7) + abort(); + if (test4(7) != 7) + abort(); + + if (test5(0) != 0) + abort(); + if (test5(2) != 0) + abort(); + if (test5(5) != 5) + abort(); + if (test5(7) != 5) + abort(); + + if (test6(0) != 0) + abort(); + if (test6(2) != 2) + abort(); + if (test6(5) != 5) + abort(); + if (test6(7) != 7) + abort(); + + return 0; +} + -- 2.30.2