From 576bc6e9e0b985c73ad30bda5ceda80a739dcbee Mon Sep 17 00:00:00 2001 From: law Date: Thu, 16 Aug 2018 16:33:43 +0000 Subject: [PATCH] * expmed.h (canonicalize_comparison): New declaration. * expmed.c (canonicalize_comparison, equivalent_cmp_code): New function. * expmed.c (emit_store_flag_1): Add call to canonicalize_comparison. * optabs.c (prepare_cmp_insn): Likewise. * rtl.h (unsigned_condition_p): New function which checks if a comparison operator is unsigned. * gcc.target/aarch64/imm_choice_comparison.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263591 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 9 +++ gcc/expmed.c | 93 ++++++++++++++++++++++ gcc/expmed.h | 2 + gcc/optabs.c | 3 + gcc/rtl.h | 9 +++ gcc/testsuite/ChangeLog | 4 + .../gcc.target/aarch64/imm_choice_comparison.c | 54 +++++++++++++ 7 files changed, 174 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/imm_choice_comparison.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fdac7e758c9..0e1125595f1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2018-08-16 Vlad Lazar + + * expmed.h (canonicalize_comparison): New declaration. + * expmed.c (canonicalize_comparison, equivalent_cmp_code): New function. + * expmed.c (emit_store_flag_1): Add call to canonicalize_comparison. + * optabs.c (prepare_cmp_insn): Likewise. + * rtl.h (unsigned_condition_p): New function which checks if a + comparison operator is unsigned. + 2018-08-16 Nathan Sidwell * config/rs6000/rs6000-c.c (rs6000_macro_to_expend): Use cpp_macro_p. diff --git a/gcc/expmed.c b/gcc/expmed.c index 101e7b88107..be9f0ec9011 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -5541,6 +5541,9 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1, if (mode == VOIDmode) mode = GET_MODE (op0); + if (CONST_SCALAR_INT_P (op1)) + canonicalize_comparison (mode, &code, &op1); + /* For some comparisons with 1 and -1, we can convert this to comparisons with zero. This will often produce more opportunities for store-flag insns. */ @@ -6161,6 +6164,96 @@ emit_store_flag_force (rtx target, enum rtx_code code, rtx op0, rtx op1, return target; } + +/* Helper function for canonicalize_cmp_for_target. Swap between inclusive + and exclusive ranges in order to create an equivalent comparison. See + canonicalize_cmp_for_target for the possible cases. */ + +static enum rtx_code +equivalent_cmp_code (enum rtx_code code) +{ + switch (code) + { + case GT: + return GE; + case GE: + return GT; + case LT: + return LE; + case LE: + return LT; + case GTU: + return GEU; + case GEU: + return GTU; + case LTU: + return LEU; + case LEU: + return LTU; + + default: + return code; + } +} + +/* Choose the more appropiate immediate in scalar integer comparisons. The + purpose of this is to end up with an immediate which can be loaded into a + register in fewer moves, if possible. + + For each integer comparison there exists an equivalent choice: + i) a > b or a >= b + 1 + ii) a <= b or a < b + 1 + iii) a >= b or a > b - 1 + iv) a < b or a <= b - 1 + + MODE is the mode of the first operand. + CODE points to the comparison code. + IMM points to the rtx containing the immediate. *IMM must satisfy + CONST_SCALAR_INT_P on entry and continues to satisfy CONST_SCALAR_INT_P + on exit. */ + +void +canonicalize_comparison (machine_mode mode, enum rtx_code *code, rtx *imm) +{ + if (!SCALAR_INT_MODE_P (mode)) + return; + + int to_add = 0; + enum signop sgn = unsigned_condition_p (*code) ? UNSIGNED : SIGNED; + + /* Extract the immediate value from the rtx. */ + wide_int imm_val = rtx_mode_t (*imm, mode); + + if (*code == GT || *code == GTU || *code == LE || *code == LEU) + to_add = 1; + else if (*code == GE || *code == GEU || *code == LT || *code == LTU) + to_add = -1; + else + return; + + /* Check for overflow/underflow in the case of signed values and + wrapping around in the case of unsigned values. If any occur + cancel the optimization. */ + wi::overflow_type overflow = wi::OVF_NONE; + wide_int imm_modif = wi::add (imm_val, to_add, sgn, &overflow); + if (overflow) + return; + + rtx reg = gen_rtx_REG (mode, LAST_VIRTUAL_REGISTER + 1); + rtx new_imm = immed_wide_int_const (imm_modif, mode); + + rtx_insn *old_rtx = gen_move_insn (reg, *imm); + rtx_insn *new_rtx = gen_move_insn (reg, new_imm); + + /* Update the immediate and the code. */ + if (insn_cost (old_rtx, true) > insn_cost (new_rtx, true)) + { + *code = equivalent_cmp_code (*code); + *imm = new_imm; + } +} + + /* Perform possibly multi-word comparison and conditional jump to LABEL if ARG1 OP ARG2 true where ARG1 and ARG2 are of mode MODE. This is diff --git a/gcc/expmed.h b/gcc/expmed.h index 2890d9c9bbd..cc247c4379e 100644 --- a/gcc/expmed.h +++ b/gcc/expmed.h @@ -702,6 +702,8 @@ extern rtx emit_store_flag (rtx, enum rtx_code, rtx, rtx, machine_mode, extern rtx emit_store_flag_force (rtx, enum rtx_code, rtx, rtx, machine_mode, int, int); +extern void canonicalize_comparison (machine_mode, enum rtx_code *, rtx *); + /* Choose a minimal N + 1 bit approximation to 1/D that can be used to replace division by D, and put the least significant N bits of the result in *MULTIPLIER_PTR and return the most significant bit. */ diff --git a/gcc/optabs.c b/gcc/optabs.c index cadf4676c98..6052222c90c 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -3812,6 +3812,9 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size, gcc_assert (methods == OPTAB_DIRECT || methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN); + if (CONST_SCALAR_INT_P (y)) + canonicalize_comparison (mode, &comparison, &y); + /* If we are optimizing, force expensive constants into a register. */ if (CONSTANT_P (x) && optimize && (rtx_cost (x, mode, COMPARE, 0, optimize_insn_for_speed_p ()) diff --git a/gcc/rtl.h b/gcc/rtl.h index d549b0aad0b..68d3ceab29f 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -3310,6 +3310,15 @@ extern enum rtx_code unsigned_condition (enum rtx_code); extern enum rtx_code signed_condition (enum rtx_code); extern void mark_jump_label (rtx, rtx_insn *, int); +/* Return true if integer comparison operator CODE interprets its operands + as unsigned. */ + +inline bool +unsigned_condition_p (enum rtx_code code) +{ + return unsigned_condition (code) == code; +} + /* In jump.c */ extern rtx_insn *delete_related_insns (rtx); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6311e0bacc7..e0f48aba8cf 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2018-08-16 Vlad Lazar + + * gcc.target/aarch64/imm_choice_comparison.c: New test. + 2018-08-16 Iain Sandoe * gcc.dg/memcmp-1.c (lib_memcmp): Apply __USER_LABEL_PREFIX__. diff --git a/gcc/testsuite/gcc.target/aarch64/imm_choice_comparison.c b/gcc/testsuite/gcc.target/aarch64/imm_choice_comparison.c new file mode 100644 index 00000000000..ebc44d6dbc7 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/imm_choice_comparison.c @@ -0,0 +1,54 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +/* Go from four moves to two. */ + +int +foo (long long x) +{ + return x <= 0x1999999999999998; +} + +int +GT (unsigned int x) +{ + return x > 0xfefffffe; +} + +int +LE (unsigned int x) +{ + return x <= 0xfefffffe; +} + +int +GE (long long x) +{ + return x >= 0xff000000; +} + +int +LT (int x) +{ + return x < 0xff000000; +} + +/* Optimize the immediate in conditionals. */ + +int +check (int x, int y) +{ + if (x > y && GT (x)) + return 100; + + return x; +} + +int +tern (int x) +{ + return x >= 0xff000000 ? 5 : -3; +} + +/* baz produces one movk instruction. */ +/* { dg-final { scan-assembler-times "movk" 1 } } */ -- 2.11.4.GIT