diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2a84e345669..38ac22c9a4f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2009-09-28 Easwaran Raman + + * ifcvt.c (noce_try_abs): Recognize pattern and call + expand_one_cmpl_abs_nojump. + * optabs.c (expand_one_cmpl_abs_nojump): New function. + * optabs.h (expand_one_cmpl_abs_nojump): Declare. + 2009-09-28 Ian Lance Taylor PR middle-end/40500 diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 1cf2608a177..1ef2d21f903 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -1744,13 +1744,16 @@ noce_try_minmax (struct noce_if_info *if_info) return TRUE; } -/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", etc. */ +/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", + "if (a < 0) x = ~a; else x = a;" to "x = one_cmpl_abs(a);", + etc. */ static int noce_try_abs (struct noce_if_info *if_info) { rtx cond, earliest, target, seq, a, b, c; int negate; + bool one_cmpl = false; /* Reject modes with signed zeros. */ if (HONOR_SIGNED_ZEROS (GET_MODE (if_info->x))) @@ -1768,6 +1771,17 @@ noce_try_abs (struct noce_if_info *if_info) c = a; a = b; b = c; negate = 1; } + else if (GET_CODE (a) == NOT && rtx_equal_p (XEXP (a, 0), b)) + { + negate = 0; + one_cmpl = true; + } + else if (GET_CODE (b) == NOT && rtx_equal_p (XEXP (b, 0), a)) + { + c = a; a = b; b = c; + negate = 1; + one_cmpl = true; + } else return FALSE; @@ -1839,13 +1853,23 @@ noce_try_abs (struct noce_if_info *if_info) } start_sequence (); - - target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1); + if (one_cmpl) + target = expand_one_cmpl_abs_nojump (GET_MODE (if_info->x), b, + if_info->x); + else + target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1); /* ??? It's a quandary whether cmove would be better here, especially for integers. Perhaps combine will clean things up. */ if (target && negate) - target = expand_simple_unop (GET_MODE (target), NEG, target, if_info->x, 0); + { + if (one_cmpl) + target = expand_simple_unop (GET_MODE (target), NOT, target, + if_info->x, 0); + else + target = expand_simple_unop (GET_MODE (target), NEG, target, + if_info->x, 0); + } if (! target) { diff --git a/gcc/optabs.c b/gcc/optabs.c index a1adc581dc1..1c136236060 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -3488,6 +3488,60 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target, return target; } +/* Emit code to compute the one's complement absolute value of OP0 + (if (OP0 < 0) OP0 = ~OP0), with result to TARGET if convenient. + (TARGET may be NULL_RTX.) The return value says where the result + actually is to be found. + + MODE is the mode of the operand; the mode of the result is + different but can be deduced from MODE. */ + +rtx +expand_one_cmpl_abs_nojump (enum machine_mode mode, rtx op0, rtx target) +{ + rtx temp; + + /* Not applicable for floating point modes. */ + if (FLOAT_MODE_P (mode)) + return NULL_RTX; + + /* If we have a MAX insn, we can do this as MAX (x, ~x). */ + if (optab_handler (smax_optab, mode)->insn_code != CODE_FOR_nothing) + { + rtx last = get_last_insn (); + + temp = expand_unop (mode, one_cmpl_optab, op0, NULL_RTX, 0); + if (temp != 0) + temp = expand_binop (mode, smax_optab, op0, temp, target, 0, + OPTAB_WIDEN); + + if (temp != 0) + return temp; + + delete_insns_since (last); + } + + /* If this machine has expensive jumps, we can do one's complement + absolute value of X as (((signed) x >> (W-1)) ^ x). */ + + if (GET_MODE_CLASS (mode) == MODE_INT + && BRANCH_COST (optimize_insn_for_speed_p (), + false) >= 2) + { + rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, + size_int (GET_MODE_BITSIZE (mode) - 1), + NULL_RTX, 0); + + temp = expand_binop (mode, xor_optab, extended, op0, target, 0, + OPTAB_LIB_WIDEN); + + if (temp != 0) + return temp; + } + + return NULL_RTX; +} + /* A subroutine of expand_copysign, perform the copysign operation using the abs and neg primitives advertised to exist on the target. The assumption is that we have a split register file, and leaving op0 in fp registers, diff --git a/gcc/optabs.h b/gcc/optabs.h index af3ea66de87..c4acb17eedd 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -706,6 +706,9 @@ extern rtx expand_unop (enum machine_mode, optab, rtx, rtx, int); extern rtx expand_abs_nojump (enum machine_mode, rtx, rtx, int); extern rtx expand_abs (enum machine_mode, rtx, rtx, int, int); +/* Expand the one's complement absolute value operation. */ +extern rtx expand_one_cmpl_abs_nojump (enum machine_mode, rtx, rtx); + /* Expand the copysign operation. */ extern rtx expand_copysign (rtx, rtx, rtx); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7d6539aa358..c5f4438fd50 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2009-09-28 Easwaran Raman + + * gcc.target/i386/ifcvt-onecmpl-abs-1.c: New file. + * gcc.c-torture/execute/ifcvt-onecmpl-abs-1.c: New file. + 2009-09-28 Janis Johnson * g++.dg/dfp/dfp.exp: Run tests from c-c++-common/dfp. diff --git a/gcc/testsuite/gcc.c-torture/execute/ifcvt-onecmpl-abs-1.c b/gcc/testsuite/gcc.c-torture/execute/ifcvt-onecmpl-abs-1.c new file mode 100644 index 00000000000..679e552f848 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/ifcvt-onecmpl-abs-1.c @@ -0,0 +1,19 @@ + +extern void abort(void); + +__attribute__ ((noinline)) +int foo(int n) +{ + if (n < 0) + n = ~n; + + return n; +} + +int main(void) +{ + if (foo (-1) != 0) + abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/ifcvt-onecmpl-abs-1.c b/gcc/testsuite/gcc.target/i386/ifcvt-onecmpl-abs-1.c new file mode 100644 index 00000000000..736053de251 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ifcvt-onecmpl-abs-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* This test checks for if-conversion of one's complement + * abs function. */ +/* { dg-options "-O" } */ +/* { dg-final { scan-assembler "sar" } } */ +/* { dg-final { scan-assembler "xor" } } */ + +/* Check code generation for one's complement version of abs */ + +int onecmplabs(int x) +{ + if (x < 0) + x = ~x; + return x; +}