aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog4
-rw-r--r--gcc/reg-stack.c71
-rw-r--r--gcc/testsuite/ChangeLog3
-rw-r--r--gcc/testsuite/gcc.target/i386/pr70465.c12
4 files changed, 90 insertions, 0 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7bed16a..9a82c9f 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,9 @@
2017-01-26 Jakub Jelinek <jakub@redhat.com>
+ PR target/70465
+ * reg-stack.c (emit_swap_insn): Instead of fld a; fld b; fxchg %st(1);
+ emit fld b; fld a; if possible.
+
* brig-builtins.def: Update copyright years.
* config/arm/arm_acle_builtins.def: Update copyright years.
diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c
index 063ef54..7bf007c 100644
--- a/gcc/reg-stack.c
+++ b/gcc/reg-stack.c
@@ -887,6 +887,77 @@ emit_swap_insn (rtx_insn *insn, stack_ptr regstack, rtx reg)
&& REG_P (i1src) && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
+
+ /* Instead of
+ fld a
+ fld b
+ fxch %st(1)
+ just use
+ fld b
+ fld a
+ if possible. */
+
+ if (REG_P (i1dest)
+ && REGNO (i1dest) == FIRST_STACK_REG
+ && MEM_P (SET_SRC (i1set))
+ && !side_effects_p (SET_SRC (i1set))
+ && hard_regno == FIRST_STACK_REG + 1
+ && i1 != BB_HEAD (current_block))
+ {
+ /* i1 is the last insn that involves stack regs before insn, and
+ is known to be a load without other side-effects, i.e. fld b
+ in the above comment. */
+ rtx_insn *i2 = NULL;
+ rtx i2set;
+ rtx_insn *tmp = PREV_INSN (i1);
+ rtx_insn *limit = PREV_INSN (BB_HEAD (current_block));
+ /* Find the previous insn involving stack regs, but don't pass a
+ block boundary. */
+ while (tmp != limit)
+ {
+ if (LABEL_P (tmp)
+ || CALL_P (tmp)
+ || NOTE_INSN_BASIC_BLOCK_P (tmp)
+ || (NONJUMP_INSN_P (tmp)
+ && stack_regs_mentioned (tmp)))
+ {
+ i2 = tmp;
+ break;
+ }
+ tmp = PREV_INSN (tmp);
+ }
+ if (i2 != NULL_RTX
+ && (i2set = single_set (i2)) != NULL_RTX)
+ {
+ rtx i2dest = *get_true_reg (&SET_DEST (i2set));
+ /* If the last two insns before insn that involve
+ stack regs are loads, where the latter (i1)
+ pushes onto the register stack and thus
+ moves the value from the first load (i2) from
+ %st to %st(1), consider swapping them. */
+ if (REG_P (i2dest)
+ && REGNO (i2dest) == FIRST_STACK_REG
+ && MEM_P (SET_SRC (i2set))
+ /* Ensure i2 doesn't have other side-effects. */
+ && !side_effects_p (SET_SRC (i2set))
+ /* And that the two instructions can actually be
+ swapped, i.e. there shouldn't be any stores
+ in between i2 and i1 that might alias with
+ the i1 memory, and the memory address can't
+ use registers set in between i2 and i1. */
+ && !modified_between_p (SET_SRC (i1set), i2, i1))
+ {
+ /* Move i1 (fld b above) right before i2 (fld a
+ above. */
+ remove_insn (i1);
+ SET_PREV_INSN (i1) = NULL_RTX;
+ SET_NEXT_INSN (i1) = NULL_RTX;
+ set_block_for_insn (i1, NULL);
+ emit_insn_before (i1, i2);
+ return;
+ }
+ }
+ }
}
/* Avoid emitting the swap if this is the first register stack insn
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 46f8469..b2d3af0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,8 @@
2017-01-26 Jakub Jelinek <jakub@redhat.com>
+ PR target/70465
+ * gcc.target/i386/pr70465.c: New test.
+
* brig.dg/dg.exp: Update copyright years.
* lib/brig-dg.exp: Update copyright years.
* lib/brig.exp: Update copyright years.
diff --git a/gcc/testsuite/gcc.target/i386/pr70465.c b/gcc/testsuite/gcc.target/i386/pr70465.c
new file mode 100644
index 0000000..2d45e1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr70465.c
@@ -0,0 +1,12 @@
+/* PR target/70465 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfpmath=387 -fomit-frame-pointer" } */
+/* { dg-final { scan-assembler-not "fxch\t%st.1" } } */
+
+double
+atan2 (double y, double x)
+{
+ double res = 0.0;
+ asm ("fpatan" : "=t" (res) : "u" (y), "0" (x) : "st(1)");
+ return res;
+}