aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyrylo Tkachov <kyrylo.tkachov@arm.com>2017-10-14 23:07:24 +0000
committerMichael Collison <michael.collison@arm.com>2017-10-14 23:07:24 +0000
commit094f35c3c96b893ca4050ebf20476040cb156552 (patch)
tree2d4a9ed6932ed48cabf52eebf5a7d1859f58f57c
parent93a1adf6e748ff8906718e7cfe9dffecbc7cefa9 (diff)
2017-10-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
Michael Collison <michael.collison@arm.com> * compare-elim.c: Include emit-rtl.h. (can_merge_compare_into_arith): New function. (try_validate_parallel): Likewise. (try_merge_compare): Likewise. (try_eliminate_compare): Call the above when no previous clobber is available. (execute_compare_elim_after_reload): Add DF_UD_CHAIN and DF_DU_CHAIN dataflow problems. 2017-10-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com> Michael Collison <michael.collison@arm.com> * gcc.target/aarch64/cmpelim_mult_uses_1.c: New test. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@253764 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog12
-rw-r--r--gcc/compare-elim.c143
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.target/aarch64/cmpelim_mult_uses_1.c17
4 files changed, 176 insertions, 1 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 1b3cacd832f..8509b499485 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2017-10-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
+ Michael Collison <michael.collison@arm.com>
+
+ * compare-elim.c: Include emit-rtl.h.
+ (can_merge_compare_into_arith): New function.
+ (try_validate_parallel): Likewise.
+ (try_merge_compare): Likewise.
+ (try_eliminate_compare): Call the above when no previous clobber
+ is available.
+ (execute_compare_elim_after_reload): Add DF_UD_CHAIN and DF_DU_CHAIN
+ dataflow problems.
+
2017-10-14 Jakub Jelinek <jakub@redhat.com>
PR middle-end/62263
diff --git a/gcc/compare-elim.c b/gcc/compare-elim.c
index 7e557a245b5..794a452f98b 100644
--- a/gcc/compare-elim.c
+++ b/gcc/compare-elim.c
@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "insn-config.h"
#include "recog.h"
+#include "emit-rtl.h"
#include "cfgrtl.h"
#include "tree-pass.h"
#include "domwalk.h"
@@ -579,6 +580,143 @@ equivalent_reg_at_start (rtx reg, rtx_insn *end, rtx_insn *start)
return reg;
}
+/* Return true if it is okay to merge the comparison CMP_INSN with
+ the instruction ARITH_INSN. Both instructions are assumed to be in the
+ same basic block with ARITH_INSN appearing before CMP_INSN. This checks
+ that there are no uses or defs of the condition flags or control flow
+ changes between the two instructions. */
+
+static bool
+can_merge_compare_into_arith (rtx_insn *cmp_insn, rtx_insn *arith_insn)
+{
+ for (rtx_insn *insn = PREV_INSN (cmp_insn);
+ insn && insn != arith_insn;
+ insn = PREV_INSN (insn))
+ {
+ if (!NONDEBUG_INSN_P (insn))
+ continue;
+ /* Bail if there are jumps or calls in between. */
+ if (!NONJUMP_INSN_P (insn))
+ return false;
+
+ /* Bail on old-style asm statements because they lack
+ data flow information. */
+ if (GET_CODE (PATTERN (insn)) == ASM_INPUT)
+ return false;
+
+ df_ref ref;
+ /* Find a USE of the flags register. */
+ FOR_EACH_INSN_USE (ref, insn)
+ if (DF_REF_REGNO (ref) == targetm.flags_regnum)
+ return false;
+
+ /* Find a DEF of the flags register. */
+ FOR_EACH_INSN_DEF (ref, insn)
+ if (DF_REF_REGNO (ref) == targetm.flags_regnum)
+ return false;
+ }
+ return true;
+}
+
+/* Given two SET expressions, SET_A and SET_B determine whether they form
+ a recognizable pattern when emitted in parallel. Return that parallel
+ if so. Otherwise return NULL. */
+
+static rtx
+try_validate_parallel (rtx set_a, rtx set_b)
+{
+ rtx par
+ = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set_a, set_b));
+
+ rtx_insn *insn;
+ insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, par, 0, -1, 0);
+
+ return recog_memoized (insn) > 0 ? par : NULL_RTX;
+}
+
+/* For a comparison instruction described by CMP check if it compares a
+ register with zero i.e. it is of the form CC := CMP R1, 0.
+ If it is, find the instruction defining R1 (say I1) and try to create a
+ PARALLEL consisting of I1 and the comparison, representing a flag-setting
+ arithmetic instruction. Example:
+ I1: R1 := R2 + R3
+ <instructions that don't read the condition register>
+ I2: CC := CMP R1 0
+ I2 can be merged with I1 into:
+ I1: { R1 := R2 + R3 ; CC := CMP (R2 + R3) 0 }
+ This catches cases where R1 is used between I1 and I2 and therefore
+ combine and other RTL optimisations will not try to propagate it into
+ I2. Return true if we succeeded in merging CMP. */
+
+static bool
+try_merge_compare (struct comparison *cmp)
+{
+ rtx_insn *cmp_insn = cmp->insn;
+
+ if (!REG_P (cmp->in_a) || cmp->in_b != const0_rtx)
+ return false;
+ rtx in_a = cmp->in_a;
+ df_ref use;
+
+ FOR_EACH_INSN_USE (use, cmp_insn)
+ if (DF_REF_REGNO (use) == REGNO (in_a))
+ break;
+ if (!use)
+ return false;
+
+ /* Validate the data flow information before attempting to
+ find the instruction that defines in_a. */
+
+ struct df_link *ref_chain;
+ ref_chain = DF_REF_CHAIN (use);
+ if (!ref_chain || !ref_chain->ref
+ || !DF_REF_INSN_INFO (ref_chain->ref) || ref_chain->next != NULL)
+ return false;
+
+ rtx_insn *def_insn = DF_REF_INSN (ref_chain->ref);
+ /* We found the insn that defines in_a. Only consider the cases where
+ it is in the same block as the comparison. */
+ if (BLOCK_FOR_INSN (cmp_insn) != BLOCK_FOR_INSN (def_insn))
+ return false;
+
+ rtx set = single_set (def_insn);
+ if (!set)
+ return false;
+
+ if (!can_merge_compare_into_arith (cmp_insn, def_insn))
+ return false;
+
+ rtx src = SET_SRC (set);
+ rtx flags = maybe_select_cc_mode (cmp, src, CONST0_RTX (GET_MODE (src)));
+ if (!flags)
+ {
+ /* We may already have a change group going through maybe_select_cc_mode.
+ Discard it properly. */
+ cancel_changes (0);
+ return false;
+ }
+
+ rtx flag_set
+ = gen_rtx_SET (flags, gen_rtx_COMPARE (GET_MODE (flags),
+ copy_rtx (src),
+ CONST0_RTX (GET_MODE (src))));
+ rtx arith_set = copy_rtx (PATTERN (def_insn));
+ rtx par = try_validate_parallel (flag_set, arith_set);
+ if (!par)
+ {
+ /* We may already have a change group going through maybe_select_cc_mode.
+ Discard it properly. */
+ cancel_changes (0);
+ return false;
+ }
+ if (!apply_change_group ())
+ return false;
+ emit_insn_after (par, def_insn);
+ delete_insn (def_insn);
+ delete_insn (cmp->insn);
+ return true;
+}
+
/* Attempt to replace a comparison with a prior arithmetic insn that can
compute the same flags value as the comparison itself. Return true if
successful, having made all rtl modifications necessary. */
@@ -588,7 +726,9 @@ try_eliminate_compare (struct comparison *cmp)
{
rtx flags, in_a, in_b, cmp_src;
- /* We must have found an interesting "clobber" preceding the compare. */
+ if (try_merge_compare (cmp))
+ return true;
+
if (cmp->prev_clobber == NULL)
return false;
@@ -714,6 +854,7 @@ try_eliminate_compare (struct comparison *cmp)
static unsigned int
execute_compare_elim_after_reload (void)
{
+ df_chain_add_problem (DF_UD_CHAIN + DF_DU_CHAIN);
df_analyze ();
gcc_checking_assert (!all_compares.exists ());
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index c5bbf3e6022..d88bfb7e63b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-10-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
+ Michael Collison <michael.collison@arm.com>
+
+ * gcc.target/aarch64/cmpelim_mult_uses_1.c: New test.
+
2017-10-14 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/80908
diff --git a/gcc/testsuite/gcc.target/aarch64/cmpelim_mult_uses_1.c b/gcc/testsuite/gcc.target/aarch64/cmpelim_mult_uses_1.c
new file mode 100644
index 00000000000..953c388037f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/cmpelim_mult_uses_1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* X is both compared against zero and used. Make sure we can still
+ generate an ADDS and avoid an explicit comparison against zero. */
+
+int
+foo (int x, int y)
+{
+ x += y;
+ if (x != 0)
+ x = x + 2;
+ return x;
+}
+
+/* { dg-final { scan-assembler-times "adds\\tw\[0-9\]+, w\[0-9\]+, w\[0-9\]+" 1 } } */
+/* { dg-final { scan-assembler-not "cmp\\tw\[0-9\]+, 0" } } */