The yearly m68k day or two of bugfixing for the retro-computing folks is
under way. It's earlier than last year, so I can do more than just fix
regressions.
This BZ is a simple request to optimize sequences like:
moveq #1,%d1
cmp.l %d0, %d1
beq/bne
into
subq.l #1, %d0
beq/bne
When d0/d1 are both dead at the cmp.l.
Similarly for small negative numbers which get turned into an addq.l.
I've chosen to implement this as a 3 insn -> 3 insn peephole2. That may
not seem useful at first. But when done right, the 2nd insn (the
compare) becomes trivial for final to eliminate resulting in the desired
code.
Each time this applies we save 2 bytes and some number of cycles
depending on the particular m68k implementation we're on.
This hits just over 4000 times building newlib. Of course we have so
many multilib configurations that any one target is going to see
considerably less impact.
Tested with m68k.exp in the GCC testsuite and by building GCC's target
libraries as well as newlib/libgloss for the m68k-elf toolchain
(libgfortran doesn't build due to an unrelated issue).
It'll get further testing once I start my yearly m68k bootstrap. But
obviously that'll be running for a good long while.
Installing on the trunk.
Jeff
commit fe059a93ff2768570cab30c2bcf58e71cfb59a66
Author: law <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri Nov 18 21:52:32 2016 +0000
PR target/25112
* config/m68k/m68k.c (moveq feeding equality comparison): New
peepholes.
* config/m68k/predicates.md (addq_subq_operand): New predicate.
(equality_comparison_operator): Likewise.
PR target/25112
* gcc.target/m68k/pr25112: New test.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@242605
138bc75d-0d04-0410-961f-82ee72b054a4
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index ff3da21..534ef4b 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+2016-11-18 Jeff Law <l...@redhat.com>
+
+ PR target/25112
+ * config/m68k/m68k.c (moveq feeding equality comparison): New
+ peepholes.
+ * config/m68k/predicates.md (addq_subq_operand): New predicate.
+ (equality_comparison_operator): Likewise.
+
2016-11-18 Richard Sandiford <richard.sandif...@arm.com>
* rtlanal.c (load_extend_op): Move to...
diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md
index 3d7895d..7b7f373 100644
--- a/gcc/config/m68k/m68k.md
+++ b/gcc/config/m68k/m68k.md
@@ -7641,6 +7641,46 @@
(const_int 0))]
"operands[5] = (operands[0] == operands[3]) ? operands[4] : operands[3];")
+;; We want to turn
+;; moveq const,dX
+;; cmp.l dX,dY
+;; je/jne
+;;
+;; into
+;; addq/subq -const,dY
+;; cmp.l dY, 0
+;; je/jne
+;;
+;; dX and dY must both be dead at the end of the sequence and the constant
+;; must be valid for addq/subq.
+;;
+;; Essentially we're making it trivial for final to realize the comparison
+;; is not needed
+;;
+;; Testing has shown a variant where the operands are reversed in the
+;; comparison never hits, so I have not included that variant.
+;;
+
+(define_peephole2
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "addq_subq_operand" ""))
+ (set (cc0) (compare (match_operand:SI 2 "register_operand" "")
+ (match_dup 0)))
+ (set (pc) (if_then_else (match_operator 5 "equality_comparison_operator"
+ [(cc0) (const_int 0)])
+ (match_operand 3 "pc_or_label_operand")
+ (match_operand 4 "pc_or_label_operand")))]
+ "peep2_reg_dead_p (2, operands[0])
+ && peep2_reg_dead_p (2, operands[2])
+ && (operands[3] == pc_rtx || operands[4] == pc_rtx)
+ && DATA_REG_P (operands[2])"
+ [(set (match_dup 2) (plus:SI (match_dup 2) (match_dup 6)))
+ (set (cc0) (compare (match_dup 2) (const_int 0)))
+ (set (pc) (if_then_else (match_op_dup 5 [(cc0) (const_int 0)])
+ (match_dup 3)
+ (match_dup 4)))]
+ "operands[6] = GEN_INT (-INTVAL (operands[1]));")
+
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "pow2_m1_operand" ""))
diff --git a/gcc/config/m68k/predicates.md b/gcc/config/m68k/predicates.md
index 186436c..bfb548a 100644
--- a/gcc/config/m68k/predicates.md
+++ b/gcc/config/m68k/predicates.md
@@ -245,6 +245,18 @@
|| reload_completed));
})
+;; Used to detect constants that are valid for addq/subq instructions
+(define_predicate "addq_subq_operand"
+ (match_code "const_int")
+{
+ return ((INTVAL (op) <= 8 && INTVAL (op) > 0)
+ || (INTVAL (op) >= -8 && INTVAL (op) < 0));
+})
+
+;; Used to detect equality and non-equality operators
+(define_predicate "equality_comparison_operator"
+ (match_code "eq,ne"))
+
;; Used to detect when an operand is either a register
;; or a constant that is all ones in its lower bits.
;; Used by insv pattern to help detect when we're initializing
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index d6b70fa..729c2e9 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2016-11-18 Jeff Law <l...@redhat.com>
+
+ PR target/25112
+ * gcc.target/m68k/pr25112: New test.
+
2016-11-18 David Edelsohn <dje....@gmail.com>
* gcc.dg/tree-ssa/pr71179.c: Prune ABI message.
diff --git a/gcc/testsuite/gcc.target/m68k/pr25112.c
b/gcc/testsuite/gcc.target/m68k/pr25112.c
new file mode 100644
index 0000000..0633cc1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/m68k/pr25112.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* { dg-final { scan-assembler-times "addq" 16 } } */
+/* { dg-final { scan-assembler-times "subq" 16 } } */
+/* { dg-final { scan-assembler-times "moveq" 4 } } */
+extern int bar (void);
+
+#define FOO(x) \
+ void foo##x (void) { int a = bar (); if (a == x) bar (); } \
+ void bar##x (void) { int a = bar (); if (a == -x) bar (); } \
+ void foon##x (void) { int a = bar (); if (a != x) bar (); } \
+ void barn##x (void) { int a = bar (); if (a != -x) bar (); } \
+
+
+FOO (1)
+FOO (2)
+FOO (3)
+FOO (4)
+FOO (5)
+FOO (6)
+FOO (7)
+FOO (8)
+FOO (9)
+
+