Hi all, This patch tries to avoid materialising an immediate when comparison has shown that it is already loaded. This is performed during ifcvt and only when the target has the conditional negate or inverse optabs, which for now is only aarch64.
Thus for the code: int foo (int a, int b) { return a == 5 ? -5 : b; } we currently emit on aarch64: foo: mov w2, -5 cmp w0, 5 csel w0, w1, w2, ne ret but with this patch we emit: foo: cmp w0, 5 csneg w0, w1, w0, ne ret Bootstrapped and tested on arm, aarch64, x86_64. Ok for trunk? Thanks, Kyrill P.S. The simpler form of the transformation: X == CST ? CST : Y --> X == CST ? X : Y could also be beneficial IMO but I found that it can cause bad codegen in combine due to extending the live range of X. I'm still working on a way to work around that, but that is a separate piece of work anyway. 2016-09-28 Kyrylo Tkachov <kyrylo.tkac...@arm.com> * ifcvt.c (noce_try_avoid_const_materialization): New function. (noce_process_if_block): Use it. 2016-09-28 Kyrylo Tkachov <kyrylo.tkac...@arm.com> * gcc.target/aarch64/ifcvt_avoid_const_materialization_1.c: New test.
diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 24542f008485e6c28e068030fa301f2ce040efc1..203cfe98f82a49c35520a2cb16d2fa09d4c85c72 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -1313,6 +1313,84 @@ noce_try_inverse_constants (struct noce_if_info *if_info) return false; } +/* Try to avoid materializing a constant if we know it's in one of the + registers. For example: + (X == CST) ? -CST : Y --> (X == CST) ? -X : Y. + Do this only if conditional negation is available. + Similar for bitwise NOT. */ + +static bool +noce_try_avoid_const_materialization (struct noce_if_info *if_info) +{ + if (!noce_simple_bbs (if_info)) + return false; + + rtx cond = if_info->cond; + rtx a = if_info->a; + rtx b = if_info->b; + rtx_code code = GET_CODE (cond); + machine_mode mode = GET_MODE (if_info->x); + + if (!(code == EQ || code == NE) + || !REG_P (XEXP (cond, 0)) + || !REG_P (if_info->x) + || GET_MODE (XEXP (cond, 0)) != mode + || !CONST_INT_P (XEXP (cond, 1))) + return false; + + rtx cst = XEXP (cond, 1); + if (cst == CONST0_RTX (mode)) + return false; + + rtx non_cst = XEXP (cond, 0); + rtx eq_side = code == EQ ? b : a; + if (!CONST_INT_P (eq_side)) + return false; + + HOST_WIDE_INT cstval = INTVAL (cst); + HOST_WIDE_INT eq_side_val = INTVAL (eq_side); + + rtx_code op_code; + if (eq_side_val == ~cstval) + op_code = NOT; + else if (eq_side_val != HOST_WIDE_INT_MIN && (cstval == -eq_side_val)) + op_code = NEG; + else + return false; + + /* By the rules of the negcc/notcc optabs must happen when the COND is true, + in this case when register in COND is equal to CST so always set the + comparison to EQ. */ + if (code == NE) + { + a = non_cst; + cond = gen_rtx_fmt_ee (EQ, GET_MODE (cond), non_cst, cst); + } + else + b = non_cst; + + start_sequence (); + rtx target + = emit_conditional_neg_or_complement (if_info->x, op_code, mode, + cond, a, b); + if (!target) + { + end_sequence (); + return false; + } + + if (target != if_info->x) + noce_emit_move_insn (if_info->x, target); + + rtx_insn *seq = end_ifcvt_sequence (if_info); + if (!seq) + return false; + + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_avoid_const_materialization"; + return true; +} /* Convert "if (test) x = a; else x = b", for A and B constant. Also allow A = y + c1, B = y + c2, with a common y between A @@ -3606,6 +3684,8 @@ noce_process_if_block (struct noce_if_info *if_info) goto success; if (noce_try_inverse_constants (if_info)) goto success; + if (noce_try_avoid_const_materialization (if_info)) + goto success; if (!targetm.have_conditional_execution () && noce_try_store_flag_constants (if_info)) goto success; diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_avoid_const_materialization_1.c b/gcc/testsuite/gcc.target/aarch64/ifcvt_avoid_const_materialization_1.c new file mode 100644 index 0000000000000000000000000000000000000000..b2a05eaff606a54afc40c3899ad5926c889283dc --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_avoid_const_materialization_1.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +/* Check that we avoid moving the immediate into a register + if comparison has shown that the inverse or negated form is + already in one of the registers. */ + +int +foo (int a, int b) +{ + return a == 5 ? -5 : b; +} + +int +bar (int a, int b) +{ + return a != 5 ? b : ~5; +} + +/* { dg-final { scan-assembler-not "mov\\tw\[0-9\]+" } } */ +/* { dg-final { scan-assembler-times "csneg\\tw\[0-9\]+" 1 } } */ +/* { dg-final { scan-assembler-times "csinv\\tw\[0-9\]+" 1 } } */