summaryrefslogtreecommitdiff
path: root/gcc/combine.c
diff options
context:
space:
mode:
authorsegher <segher@138bc75d-0d04-0410-961f-82ee72b054a4>2014-12-01 22:36:12 +0000
committersegher <segher@138bc75d-0d04-0410-961f-82ee72b054a4>2014-12-01 22:36:12 +0000
commit518a433d6ff659b766ce4f52cb5edb15a72123ed (patch)
tree00c031aeb8738912134e130d423b4a36ca3320ed /gcc/combine.c
parentf5627b55e97dc0d28500641f10c98685d3d28972 (diff)
downloadgcc-518a433d6ff659b766ce4f52cb5edb15a72123ed.tar.gz
* combine.c (is_parallel_of_n_reg_sets): New function.
(can_split_parallel_of_n_reg_sets): New function. (try_combine): If I2 is a PARALLEL of two SETs, split it into two insns if possible. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@218248 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/combine.c')
-rw-r--r--gcc/combine.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/gcc/combine.c b/gcc/combine.c
index afcb91eedd0..b9319023c34 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -2461,6 +2461,59 @@ update_cfg_for_uncondjump (rtx_insn *insn)
}
}
+/* Return whether INSN is a PARALLEL of exactly N register SETs followed
+ by an arbitrary number of CLOBBERs. */
+static bool
+is_parallel_of_n_reg_sets (rtx_insn *insn, int n)
+{
+ rtx pat = PATTERN (insn);
+
+ if (GET_CODE (pat) != PARALLEL)
+ return false;
+
+ int len = XVECLEN (pat, 0);
+ if (len < n)
+ return false;
+
+ int i;
+ for (i = 0; i < n; i++)
+ if (GET_CODE (XVECEXP (pat, 0, i)) != SET
+ || !REG_P (SET_DEST (XVECEXP (pat, 0, i))))
+ return false;
+ for ( ; i < len; i++)
+ if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER)
+ return false;
+
+ return true;
+}
+
+/* Return whether INSN, a PARALLEL of N register SETs (and maybe some
+ CLOBBERs), can be split into individual SETs in that order, without
+ changing semantics. */
+static bool
+can_split_parallel_of_n_reg_sets (rtx_insn *insn, int n)
+{
+ if (!insn_nothrow_p (insn))
+ return false;
+
+ rtx pat = PATTERN (insn);
+
+ int i, j;
+ for (i = 0; i < n; i++)
+ {
+ if (side_effects_p (SET_SRC (XVECEXP (pat, 0, i))))
+ return false;
+
+ rtx reg = SET_DEST (XVECEXP (pat, 0, i));
+
+ for (j = i + 1; j < n; j++)
+ if (reg_referenced_p (reg, XVECEXP (pat, 0, j)))
+ return false;
+ }
+
+ return true;
+}
+
/* Try to combine the insns I0, I1 and I2 into I3.
Here I0, I1 and I2 appear earlier than I3.
I0 and I1 can be zero; then we combine just I2 into I3, or I1 and I2 into
@@ -2817,6 +2870,31 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
alloc_insn_link (i1, regno, LOG_LINKS (i2)));
}
}
+
+ /* If I2 is a PARALLEL of two SETs of REGs (and perhaps some CLOBBERs),
+ make those two SETs separate I1 and I2 insns, and make an I0 that is
+ the original I1. */
+ if (i0 == 0
+ && is_parallel_of_n_reg_sets (i2, 2)
+ && can_split_parallel_of_n_reg_sets (i2, 2)
+ && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3)
+ && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3))
+ {
+ /* If there is no I1, there is no I0 either. */
+ i0 = i1;
+
+ /* We make I1 with the same INSN_UID as I2. This gives it
+ the same DF_INSN_LUID for value tracking. Our fake I1 will
+ never appear in the insn stream so giving it the same INSN_UID
+ as I2 will not cause a problem. */
+
+ i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2),
+ XVECEXP (PATTERN (i2), 0, 0), INSN_LOCATION (i2),
+ -1, NULL_RTX);
+ INSN_UID (i1) = INSN_UID (i2);
+
+ SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 1));
+ }
#endif
/* Verify that I2 and I1 are valid for combining. */