From: Kyrylo Tkachov Date: Mon, 6 Jun 2016 16:06:05 +0000 (+0000) Subject: [3/3][RTL ifcvt] PR middle-end/37780: Conditional expression with __builtin_clz(... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=36f9ad69336aeee0aa7cf2d8640fccbec8d659e1;p=gcc.git [3/3][RTL ifcvt] PR middle-end/37780: Conditional expression with __builtin_clz() should be optimized out PR middle-end/37780 * ifcvt.c (noce_try_ifelse_collapse): New function. Declare prototype. (noce_process_if_block): Call noce_try_ifelse_collapse. * simplify-rtx.c (simplify_cond_clz_ctz): New function. (simplify_ternary_operation): Use the above to simplify conditional CLZ/CTZ expressions. * gcc.c-torture/execute/pr37780.c: New test. * gcc.target/aarch64/pr37780_1.c: Likewise. * gcc.target/arm/pr37780_1.c: Likewise. From-SVN: r237141 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8a1a2a6d52f..3e6879849f2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2016-06-06 Kyrylo Tkachov + + PR middle-end/37780 + * ifcvt.c (noce_try_ifelse_collapse): New function. + Declare prototype. + (noce_process_if_block): Call noce_try_ifelse_collapse. + * simplify-rtx.c (simplify_cond_clz_ctz): New function. + (simplify_ternary_operation): Use the above to simplify + conditional CLZ/CTZ expressions. + 2016-06-06 Kyrylo Tkachov PR middle-end/37780 diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 44ae020db24..05fac71409d 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -817,6 +817,7 @@ struct noce_if_info static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int); static int noce_try_move (struct noce_if_info *); +static int noce_try_ifelse_collapse (struct noce_if_info *); static int noce_try_store_flag (struct noce_if_info *); static int noce_try_addcc (struct noce_if_info *); static int noce_try_store_flag_constants (struct noce_if_info *); @@ -1120,6 +1121,37 @@ noce_try_move (struct noce_if_info *if_info) return FALSE; } +/* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that + through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE. + If that is the case, emit the result into x. */ + +static int +noce_try_ifelse_collapse (struct noce_if_info * if_info) +{ + if (!noce_simple_bbs (if_info)) + return FALSE; + + machine_mode mode = GET_MODE (if_info->x); + rtx if_then_else = simplify_gen_ternary (IF_THEN_ELSE, mode, mode, + if_info->cond, if_info->b, + if_info->a); + + if (GET_CODE (if_then_else) == IF_THEN_ELSE) + return FALSE; + + rtx_insn *seq; + start_sequence (); + noce_emit_move_insn (if_info->x, if_then_else); + seq = end_ifcvt_sequence (if_info); + if (!seq) + return FALSE; + + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + return TRUE; +} + + /* Convert "if (test) x = 1; else x = 0". Only try 0 and STORE_FLAG_VALUE here. Other combinations will be @@ -3497,6 +3529,8 @@ noce_process_if_block (struct noce_if_info *if_info) if (noce_try_move (if_info)) goto success; + if (noce_try_ifelse_collapse (if_info)) + goto success; if (noce_try_store_flag (if_info)) goto success; if (noce_try_bitop (if_info)) diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index fdc4b366c74..2b649fa405b 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -5267,6 +5267,49 @@ simplify_const_relational_operation (enum rtx_code code, return 0; } + +/* Recognize expressions of the form (X CMP 0) ? VAL : OP (X) + where OP is CLZ or CTZ and VAL is the value from CLZ_DEFINED_VALUE_AT_ZERO + or CTZ_DEFINED_VALUE_AT_ZERO respectively and return OP (X) if the expression + can be simplified to that or NULL_RTX if not. + Assume X is compared against zero with CMP_CODE and the true + arm is TRUE_VAL and the false arm is FALSE_VAL. */ + +static rtx +simplify_cond_clz_ctz (rtx x, rtx_code cmp_code, rtx true_val, rtx false_val) +{ + if (cmp_code != EQ && cmp_code != NE) + return NULL_RTX; + + /* Result on X == 0 and X !=0 respectively. */ + rtx on_zero, on_nonzero; + if (cmp_code == EQ) + { + on_zero = true_val; + on_nonzero = false_val; + } + else + { + on_zero = false_val; + on_nonzero = true_val; + } + + rtx_code op_code = GET_CODE (on_nonzero); + if ((op_code != CLZ && op_code != CTZ) + || !rtx_equal_p (XEXP (on_nonzero, 0), x) + || !CONST_INT_P (on_zero)) + return NULL_RTX; + + HOST_WIDE_INT op_val; + machine_mode mode = GET_MODE (on_nonzero); + if (((op_code == CLZ && CLZ_DEFINED_VALUE_AT_ZERO (mode, op_val)) + || (op_code == CTZ && CTZ_DEFINED_VALUE_AT_ZERO (mode, op_val))) + && op_val == INTVAL (on_zero)) + return on_nonzero; + + return NULL_RTX; +} + /* Simplify CODE, an operation with result mode MODE and three operands, OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became @@ -5400,6 +5443,19 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode, } } + /* Convert x == 0 ? N : clz (x) into clz (x) when + CLZ_DEFINED_VALUE_AT_ZERO is defined to N for the mode of x. + Similarly for ctz (x). */ + if (COMPARISON_P (op0) && !side_effects_p (op0) + && XEXP (op0, 1) == const0_rtx) + { + rtx simplified + = simplify_cond_clz_ctz (XEXP (op0, 0), GET_CODE (op0), + op1, op2); + if (simplified) + return simplified; + } + if (COMPARISON_P (op0) && ! side_effects_p (op0)) { machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1a7e9eef59b..b68744b92f3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2016-06-06 Kyrylo Tkachov + + PR middle-end/37780 + * gcc.c-torture/execute/pr37780.c: New test. + * gcc.target/aarch64/pr37780_1.c: Likewise. + * gcc.target/arm/pr37780_1.c: Likewise. + 2016-06-06 Marek Polacek * c-c++-common/attr-may-alias-1.c: New test. diff --git a/gcc/testsuite/gcc.c-torture/execute/pr37780.c b/gcc/testsuite/gcc.c-torture/execute/pr37780.c new file mode 100644 index 00000000000..a9eca68786e --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/pr37780.c @@ -0,0 +1,49 @@ +/* PR middle-end/37780. */ + +#define VAL (8 * sizeof (int)) + +int __attribute__ ((noinline, noclone)) +fooctz (int i) +{ + return (i == 0) ? VAL : __builtin_ctz (i); +} + +int __attribute__ ((noinline, noclone)) +fooctz2 (int i) +{ + return (i != 0) ? __builtin_ctz (i) : VAL; +} + +unsigned int __attribute__ ((noinline, noclone)) +fooctz3 (unsigned int i) +{ + return (i > 0) ? __builtin_ctz (i) : VAL; +} + +int __attribute__ ((noinline, noclone)) +fooclz (int i) +{ + return (i == 0) ? VAL : __builtin_clz (i); +} + +int __attribute__ ((noinline, noclone)) +fooclz2 (int i) +{ + return (i != 0) ? __builtin_clz (i) : VAL; +} + +unsigned int __attribute__ ((noinline, noclone)) +fooclz3 (unsigned int i) +{ + return (i > 0) ? __builtin_clz (i) : VAL; +} + +int +main (void) +{ + if (fooctz (0) != VAL || fooctz2 (0) != VAL || fooctz3 (0) != VAL + || fooclz (0) != VAL || fooclz2 (0) != VAL || fooclz3 (0) != VAL) + __builtin_abort (); + + return 0; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/pr37780_1.c b/gcc/testsuite/gcc.target/aarch64/pr37780_1.c new file mode 100644 index 00000000000..97027e7479c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/pr37780_1.c @@ -0,0 +1,46 @@ +/* Test that we can remove the conditional move due to CLZ + and CTZ being defined at zero. */ + +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +int +fooctz (int i) +{ + return (i == 0) ? 32 : __builtin_ctz (i); +} + +int +fooctz2 (int i) +{ + return (i != 0) ? __builtin_ctz (i) : 32; +} + +unsigned int +fooctz3 (unsigned int i) +{ + return (i > 0) ? __builtin_ctz (i) : 32; +} + +/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */ + +int +fooclz (int i) +{ + return (i == 0) ? 32 : __builtin_clz (i); +} + +int +fooclz2 (int i) +{ + return (i != 0) ? __builtin_clz (i) : 32; +} + +unsigned int +fooclz3 (unsigned int i) +{ + return (i > 0) ? __builtin_clz (i) : 32; +} + +/* { dg-final { scan-assembler-times "clz\t" 6 } } */ +/* { dg-final { scan-assembler-not "cmp\t.*0" } } */ diff --git a/gcc/testsuite/gcc.target/arm/pr37780_1.c b/gcc/testsuite/gcc.target/arm/pr37780_1.c new file mode 100644 index 00000000000..b954fe5ceb8 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr37780_1.c @@ -0,0 +1,47 @@ +/* Test that we can remove the conditional move due to CLZ + being defined at zero. */ + +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arch_v5_ok } */ +/* { dg-options "-O2" } */ + +int +fooctz (int i) +{ + return (i == 0) ? 32 : __builtin_ctz (i); +} + +int +fooctz2 (int i) +{ + return (i != 0) ? __builtin_ctz (i) : 32; +} + +unsigned int +fooctz3 (unsigned int i) +{ + return (i > 0) ? __builtin_ctz (i) : 32; +} + +/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */ + +int +fooclz (int i) +{ + return (i == 0) ? 32 : __builtin_clz (i); +} + +int +fooclz2 (int i) +{ + return (i != 0) ? __builtin_clz (i) : 32; +} + +unsigned int +fooclz3 (unsigned int i) +{ + return (i > 0) ? __builtin_clz (i) : 32; +} + +/* { dg-final { scan-assembler-times "clz\t" 6 } } */ +/* { dg-final { scan-assembler-not "cmp\t.*0" } } */