diff options
author | Kyrylo Tkachov <kyrylo.tkachov@arm.com> | 2017-10-14 23:07:24 +0000 |
---|---|---|
committer | Michael Collison <collison@gcc.gnu.org> | 2017-10-14 23:07:24 +0000 |
commit | d7840b4702a91456a16cf1d4744c4b7a9c021138 (patch) | |
tree | 2d4a9ed6932ed48cabf52eebf5a7d1859f58f57c /gcc/compare-elim.c | |
parent | c97b789cb88ceb5b3fc2aa94506978e74503d895 (diff) |
compare-elim.c: Include emit-rtl.h.
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.
Co-Authored-By: Michael Collison <michael.collison@arm.com>
From-SVN: r253764
Diffstat (limited to 'gcc/compare-elim.c')
-rw-r--r-- | gcc/compare-elim.c | 143 |
1 files changed, 142 insertions, 1 deletions
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 ()); |