diff mbox series

[committed] Use add/sub/neg insns to eliminate compare/test insns on H8

Message ID 6bf2525a-1c31-a27b-cd80-17749275c026@redhat.com
State New
Headers show
Series [committed] Use add/sub/neg insns to eliminate compare/test insns on H8 | expand

Commit Message

Jeff Law Dec. 2, 2020, 4:49 a.m. UTC
So this is the first in an unknown number of patches to start optimizing
away unnecessary compares and test insns on the H8.    I originally had
distinct insns for setting the flags and I still use those patches for
comparison purposes, but this patch uses define_subst rather than
creating distinct patterns.

This particular patch enables using addition, subtraction and negation
insns to remove redundant compare and test instructions.  It actually
does a better job than we ever did with the old cc0 scheme.  It's got
reasonable tests for add and subtract.  Negation is a bit tougher to
build simple tests for, but I'm confident it works as instrumentation
has shown it triggering.

There is one case where I think this patch could regress the generated
code.  Specifically it over-computes the size of an addition by +-4 by 2
bytes when the addition is not used for removing a compare/test insn. 
As a result we could end up generating a longer branch than is strictly
necessary.  If this turns out to be a problem, we can certainly account
for that special case in the length computation if we need to.

Naturally this does not trigger any regressions in the testsuite.

Committed to the trunk.

Jeff
commit 7da97411b048cdd4e7941b311514f46ea53fe3a2
Author: Jeff Law <law@redhat.com>
Date:   Tue Dec 1 21:48:10 2020 -0700

    Use add/sub/neg insns to eliminate compare/test insns on H8
    
    gcc/
    
            * config/h8300/addsub.md (addqi3_clobber_flags): Rename to
            addqi3_flags and annotate with a <cczn> for define_subst.
            (addhi3_h8sx_clobber_flags): Likewise.
            (subqi3_clobber_flags, sub<mode>3_clobber_flags): Likewise.
            (neg<mode2>_clobber_flags): Similarly.
            (addsi3_clobber_flags): Similarly.  Update last argument to
            output_plussi to distinguish when we need flags or do not need
            flags.
            (addhi3_clobber_flags): Similarly.  Twiddle code for cases
            +-1, +-2 and +-4.
            * config/h8300/h8300.md: Define iterators, mode attributes and
            substitutions for use in compare/test elimination.
            * config/h8300/jumpcall.md (branch, branch_1): Use H8cc mode
            iterator to cover the different modes for the CC register.
            (branch_1_false): Likewise.
    
    gcc/testsuite
            * gcc.target/h8300/add.c: New test.
            * gcc.target/h8300/add-2.c: New test.
            * gcc.target/h8300/add-3.c: New test.
            * gcc.target/h8300/sub.c: New test.
            * gcc.target/h8300/sub-2.c: New test.
            * gcc.target/h8300/sub-3.c: New test.
diff mbox series

Patch

diff --git a/gcc/config/h8300/addsub.md b/gcc/config/h8300/addsub.md
index d0877c008bf..3585bffa9fc 100644
--- a/gcc/config/h8300/addsub.md
+++ b/gcc/config/h8300/addsub.md
@@ -19,7 +19,7 @@ 
   [(parallel [(set (match_dup 0) (plus:QI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*addqi3_clobber_flags"
+(define_insn "*addqi3_flags<cczn>"
   [(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
 	(plus:QI (match_operand:QI 1 "h8300_dst_operand" "%0")
 		 (match_operand:QI 2 "h8300_src_operand" "rQi")))
@@ -38,18 +38,41 @@ 
   [(parallel [(set (match_dup 0) (plus:HI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*addhi3_clobber_flags"
+(define_insn "*addhi3_flags<cczn>"
   [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
 	(plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0")
-		 (match_operand:HI 2 "h8300_src_operand" "L,N,J,n,r")))
+		 (match_operand:HI 2 "h8300_src_operand" "M,O,J,n,r")))
    (clobber (reg:CC CC_REG))]
   "reload_completed && !TARGET_H8300SX"
-  "@
-   adds	%2,%S0
-   subs	%G2,%S0
-   add.b	%t2,%t0
-   add.w	%T2,%T0
-   add.w	%T2,%T0"
+  "*
+  {
+    switch (which_alternative)
+      {
+      case 0:
+	return \"inc %T2,%T0\";
+      case 1:
+	return \"dec %G2,%T0\";
+      case 2:
+	return \"add.b	%t2,%t0\";
+      case 3:
+	{
+	  /* If the constant is 4 or -4 and we do not need the
+	     flags, then we can use adds/subs which is two bytes
+	     shorter.  */
+	  rtx x = XVECEXP (PATTERN (insn), 0, 1);
+	  bool clobber = GET_CODE (x) == CLOBBER;
+	  if (clobber && INTVAL (operands[2]) == 4)
+	    return \"adds	%2,%S0\";
+	  if (clobber && INTVAL (operands[2]) == -4)
+	    return \"subs	%G2,%S0\";
+	  return \"add.w	%T2,%T0\";
+	}
+      case 4:
+	return \"add.w	%T2,%T0\";
+      default:
+	gcc_unreachable ();
+      }
+  }"
   [(set_attr "length" "2,2,2,4,2")])
 
 (define_insn_and_split "*addhi3_h8sx"
@@ -62,7 +85,7 @@ 
   [(parallel [(set (match_dup 0) (plus:HI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*addhi3_h8sx_clobber_flags"
+(define_insn "*addhi3_h8sx_flags<cczn>"
   [(set (match_operand:HI 0 "h8300_dst_operand" "=rU,rU,r,rQ")
 	(plus:HI (match_operand:HI 1 "h8300_dst_operand" "%0,0,0,0")
 		 (match_operand:HI 2 "h8300_src_operand" "P3>X,P3<X,J,rQi")))
@@ -98,14 +121,15 @@ 
   [(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*addsi_clobber_flags"
+(define_insn "*addsi_flags<cczn>"
   [(set (match_operand:SI 0 "h8300_dst_operand" "=rQ,rQ")
 	(plus:SI (match_operand:SI 1 "h8300_dst_operand" "%0,0")
 		 (match_operand:SI 2 "h8300_src_operand" "i,rQ")))
    (clobber (reg:CC CC_REG))]
   "reload_completed && h8300_operands_match_p (operands)"
 {
-  return output_plussi (operands, false);
+  rtx x = XVECEXP (PATTERN (insn), 0, 1);
+  return output_plussi (operands, GET_CODE (x) != CLOBBER);
 }
   [(set (attr "length")
 	(symbol_ref "compute_plussi_length (operands, false)"))])
@@ -130,7 +154,7 @@ 
   [(parallel [(set (match_dup 0) (minus:QI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*subqi3_clobber_flags"
+(define_insn "*subqi3_flags<cczn>"
   [(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
 	(minus:QI (match_operand:QI 1 "h8300_dst_operand" "0")
 		  (match_operand:QI 2 "h8300_dst_operand" "rQ")))
@@ -149,7 +173,7 @@ 
   [(parallel [(set (match_dup 0) (minus:HSI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*sub<mode>3_clobber_flags"
+(define_insn "*sub<mode>3_flags<cczn>"
   [(set (match_operand:HSI 0 "h8300_dst_operand" "=rQ,rQ")
 	(minus:HSI (match_operand:HSI 1 "h8300_dst_operand" "0,0")
 		   (match_operand:HSI 2 "h8300_src_operand" "rQ,i")))
@@ -183,7 +207,7 @@ 
   [(parallel [(set (match_dup 0) (neg:QHSI (match_dup 1)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*neg<mode>2_clobber_flags"
+(define_insn "*neg<mode>2_flags<cczn>"
   [(set (match_operand:QHSI 0 "h8300_dst_operand" "=rQ")
 	(neg:QHSI (match_operand:QHSI 1 "h8300_dst_operand" "0")))
    (clobber (reg:CC CC_REG))]
diff --git a/gcc/config/h8300/h8300.md b/gcc/config/h8300/h8300.md
index 7c9cc324f39..932f74eb88f 100644
--- a/gcc/config/h8300/h8300.md
+++ b/gcc/config/h8300/h8300.md
@@ -134,6 +134,39 @@ 
 (define_attr "old_cc" "none,none_0hit,set_znv,set_zn,compare,clobber"
   (const_string "clobber"))
 
+;; So the idea here is to define iterators and substitutions so that we
+;; can easily modify the patterns with CC clobbers into a pattern
+;; which sets appropriate condition codes
+
+;; The modes we're supporting.  This is used when we want to generate
+;; multiple patterns where only the mode differs from a single template
+(define_mode_iterator H8cc [CC CCZN])
+
+;; This is used to generate multiple define_substs from a single
+;; template for the different variants we might have.
+(define_mode_attr cc [(CC "cc") (CCZN "cczn")])
+
+;; The primary substitution pattern.  <cc> is used to create multiple
+;; substitutions based on the CC bits that are set.
+;;
+;; The mode iterator sets the actual mode on the condition code
+;; REG expression.
+(define_subst "subst_<cc>"
+  [(set (match_operand 0 "")
+	(match_operand 1 ""))
+   (clobber (reg:CC CC_REG))]
+  ""
+  [(set (reg:H8cc CC_REG)
+	(compare:H8cc (match_dup 1) (const_int 0)))
+   (set (match_dup 0) (match_dup 1))])
+
+
+;; So when we see <cc> or <cczn> in a define_insn pattern, we'll
+;; apply the subst_cczn or subset_cc define_subst to generate a
+;; new pattern that compare-elim can use
+(define_subst_attr "cczn" "subst_cczn" "" "_cczn")
+(define_subst_attr "cc" "subst_cc" "" "_cc")
+
 ;; Type of delay slot.  NONE means the instruction has no delay slot.
 ;; JUMP means it is an unconditional jump that (if short enough)
 ;; could be implemented using bra/s.
diff --git a/gcc/config/h8300/jumpcall.md b/gcc/config/h8300/jumpcall.md
index c07dbaf999c..49d1e4312ca 100644
--- a/gcc/config/h8300/jumpcall.md
+++ b/gcc/config/h8300/jumpcall.md
@@ -23,18 +23,18 @@ 
   ""
   "#"
   "reload_completed"
-  [(set (reg:CC CC_REG)
-	(compare:CC (match_dup 1) (match_dup 2)))
+  [(set (reg:H8cc CC_REG)
+	(compare:H8cc (match_dup 1) (match_dup 2)))
    (set (pc)
 	(if_then_else (match_op_dup 0
-		       [(reg:CC CC_REG) (const_int 0)])
+		       [(reg:H8cc CC_REG) (const_int 0)])
 		      (label_ref (match_dup 3)) (pc)))]
   "")
 
 (define_insn "*branch_1"
   [(set (pc)
 	(if_then_else (match_operator 1 "comparison_operator"
-		       [(reg:CC CC_REG) (const_int 0)])
+		       [(reg:H8cc CC_REG) (const_int 0)])
 		      (label_ref (match_operand 0 "" ""))
 		      (pc)))]
   "reload_completed"
@@ -52,7 +52,7 @@ 
 (define_insn "*branch_1_false"
   [(set (pc)
 	(if_then_else (match_operator 1 "comparison_operator"
-		       [(reg:CC CC_REG) (const_int 0)])
+		       [(reg:H8cc CC_REG) (const_int 0)])
 		      (pc)
 		      (label_ref (match_operand 0 "" ""))))]
   "reload_completed"
diff --git a/gcc/testsuite/gcc.target/h8300/add-2.c b/gcc/testsuite/gcc.target/h8300/add-2.c
new file mode 100644
index 00000000000..27f9b2f0069
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/add-2.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-ms -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+#include "add.c"
diff --git a/gcc/testsuite/gcc.target/h8300/add-3.c b/gcc/testsuite/gcc.target/h8300/add-3.c
new file mode 100644
index 00000000000..20de647c944
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/add-3.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-msx -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+#include "add.c"
diff --git a/gcc/testsuite/gcc.target/h8300/add.c b/gcc/testsuite/gcc.target/h8300/add.c
new file mode 100644
index 00000000000..ebeea579f6b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/add.c
@@ -0,0 +1,118 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-mh -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+typedef unsigned char uchar;
+typedef signed char schar;
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+
+volatile void abort (void);
+
+
+#define ADD(T)\
+T addE##T (T x, T y) { T t = x + y ; if (t == 0) abort (); return t; } \
+T addNE##T (T x, T y) { T t = x + y ; if (t != 0) return t; abort (); } \
+T addGE##T (T x, T y) { T t = x + y ; if (t >= 0) abort (); return t; } \
+T addLT##T (T x, T y) { T t = x + y ; if (t < 0) abort (); return t; }
+
+#define ADDC(T,N)\
+T addEQ##N##T (T a) { T t = a + N; if (t == 0) abort (); return t; } \
+T addNE##N##T (T a) { T t = a + N; if (t != 0) return t; abort (); } \
+T addGE##N##T (T a) { T t = a + N; if (t >= 0) abort (); return t; } \
+T addLT##N##T (T a) { T t = a + N; if (t < 0) abort (); return t; }
+
+#define ADDNC(T,N)\
+T addEQN##N##T (T a) { T t = a + -N; if (t == 0) abort (); return t; } \
+T addNEN##N##T (T a) { T t = a + -N; if (t != 0) return t; abort (); } \
+T addGEN##N##T (T a) { T t = a + -N; if (t >= 0) abort (); return t; } \
+T addLTN##N##T (T a) { T t = a + -N; if (t < 0) abort (); return t; }
+
+
+ADD (schar)
+ADD (short)
+ADD (long)
+ADD (uchar)
+ADD (ushort)
+ADD (ulong)
+
+
+
+ADDC (schar,1)
+ADDC (schar,2)
+ADDC (schar,3)
+ADDC (schar,4)
+ADDC (schar,6)
+ADDC (schar,8)
+ADDNC (schar,1)
+ADDNC (schar,2)
+ADDNC (schar,3)
+ADDNC (schar,4)
+ADDNC (schar,6)
+ADDNC (schar,8)
+
+ADDC (uchar,1)
+ADDC (uchar,2)
+ADDC (uchar,3)
+ADDC (uchar,4)
+ADDC (uchar,6)
+ADDC (uchar,8)
+ADDNC (uchar,1)
+ADDNC (uchar,2)
+ADDNC (uchar,3)
+ADDNC (uchar,4)
+ADDNC (uchar,6)
+ADDNC (uchar,8)
+
+ADDC (short,1)
+ADDC (short,2)
+ADDC (short,3)
+ADDC (short,4)
+ADDC (short,6)
+ADDC (short,8)
+ADDNC (short,1)
+ADDNC (short,2)
+ADDNC (short,3)
+ADDNC (short,4)
+ADDNC (short,6)
+ADDNC (short,8)
+
+ADDC (ushort,1)
+ADDC (ushort,2)
+ADDC (ushort,3)
+ADDC (ushort,4)
+ADDC (ushort,6)
+ADDC (ushort,8)
+ADDNC (ushort,1)
+ADDNC (ushort,2)
+ADDNC (ushort,3)
+ADDNC (ushort,4)
+ADDNC (ushort,6)
+ADDNC (ushort,8)
+
+ADDC (long,1)
+ADDC (long,2)
+ADDC (long,3)
+ADDC (long,4)
+ADDC (long,6)
+ADDC (long,8)
+ADDNC (long,1)
+ADDNC (long,2)
+ADDNC (long,3)
+ADDNC (long,4)
+ADDNC (long,6)
+ADDNC (long,8)
+
+ADDC (ulong,1)
+ADDC (ulong,2)
+ADDC (ulong,3)
+ADDC (ulong,4)
+ADDC (ulong,6)
+ADDC (ulong,8)
+ADDNC (ulong,1)
+ADDNC (ulong,2)
+ADDNC (ulong,3)
+ADDNC (ulong,4)
+ADDNC (ulong,6)
+ADDNC (ulong,8)
+
diff --git a/gcc/testsuite/gcc.target/h8300/sub-2.c b/gcc/testsuite/gcc.target/h8300/sub-2.c
new file mode 100644
index 00000000000..c2914bd1d97
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/sub-2.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-ms -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+#include "sub.c"
diff --git a/gcc/testsuite/gcc.target/h8300/sub-3.c b/gcc/testsuite/gcc.target/h8300/sub-3.c
new file mode 100644
index 00000000000..72bcf04d156
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/sub-3.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-msx -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+#include "sub.c"
diff --git a/gcc/testsuite/gcc.target/h8300/sub.c b/gcc/testsuite/gcc.target/h8300/sub.c
new file mode 100644
index 00000000000..66b63ab447d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/h8300/sub.c
@@ -0,0 +1,118 @@ 
+/* { dg-do compile }  */
+/* { dg-options "-mh -mint32 -O2" }  */
+/* { dg-final { scan-assembler-not "cmp" } }  */
+
+typedef unsigned char uchar;
+typedef signed char schar;
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+
+volatile void abort (void);
+
+
+#define SUB(T)\
+T subE##T (T x, T y) { T t = x - y ; if (t == 0) abort (); return t; } \
+T subNE##T (T x, T y) { T t = x - y ; if (t != 0) return t; abort (); } \
+T subGE##T (T x, T y) { T t = x - y ; if (t >= 0) abort (); return t; } \
+T subLT##T (T x, T y) { T t = x - y ; if (t < 0) abort (); return t; }
+
+#define SUBC(T,N)\
+T subEQ##N##T (T a) { T t = a - N; if (t == 0) abort (); return t; } \
+T subNE##N##T (T a) { T t = a - N; if (t != 0) return t; abort (); } \
+T subGE##N##T (T a) { T t = a - N; if (t >= 0) abort (); return t; } \
+T subLT##N##T (T a) { T t = a - N; if (t < 0) abort (); return t; }
+
+#define SUBNC(T,N)\
+T subEQN##N##T (T a) { T t = a - -N; if (t == 0) abort (); return t; } \
+T subNEN##N##T (T a) { T t = a - -N; if (t != 0) return t; abort (); } \
+T subGEN##N##T (T a) { T t = a - -N; if (t >= 0) abort (); return t; } \
+T subLTN##N##T (T a) { T t = a - -N; if (t < 0) abort (); return t; }
+
+
+SUB (schar)
+SUB (short)
+SUB (long)
+SUB (uchar)
+SUB (ushort)
+SUB (ulong)
+
+
+
+SUBC (schar,1)
+SUBC (schar,2)
+SUBC (schar,3)
+SUBC (schar,4)
+SUBC (schar,6)
+SUBC (schar,8)
+SUBNC (schar,1)
+SUBNC (schar,2)
+SUBNC (schar,3)
+SUBNC (schar,4)
+SUBNC (schar,6)
+SUBNC (schar,8)
+
+SUBC (uchar,1)
+SUBC (uchar,2)
+SUBC (uchar,3)
+SUBC (uchar,4)
+SUBC (uchar,6)
+SUBC (uchar,8)
+SUBNC (uchar,1)
+SUBNC (uchar,2)
+SUBNC (uchar,3)
+SUBNC (uchar,4)
+SUBNC (uchar,6)
+SUBNC (uchar,8)
+
+SUBC (short,1)
+SUBC (short,2)
+SUBC (short,3)
+SUBC (short,4)
+SUBC (short,6)
+SUBC (short,8)
+SUBNC (short,1)
+SUBNC (short,2)
+SUBNC (short,3)
+SUBNC (short,4)
+SUBNC (short,6)
+SUBNC (short,8)
+
+SUBC (ushort,1)
+SUBC (ushort,2)
+SUBC (ushort,3)
+SUBC (ushort,4)
+SUBC (ushort,6)
+SUBC (ushort,8)
+SUBNC (ushort,1)
+SUBNC (ushort,2)
+SUBNC (ushort,3)
+SUBNC (ushort,4)
+SUBNC (ushort,6)
+SUBNC (ushort,8)
+
+SUBC (long,1)
+SUBC (long,2)
+SUBC (long,3)
+SUBC (long,4)
+SUBC (long,6)
+SUBC (long,8)
+SUBNC (long,1)
+SUBNC (long,2)
+SUBNC (long,3)
+SUBNC (long,4)
+SUBNC (long,6)
+SUBNC (long,8)
+
+SUBC (ulong,1)
+SUBC (ulong,2)
+SUBC (ulong,3)
+SUBC (ulong,4)
+SUBC (ulong,6)
+SUBC (ulong,8)
+SUBNC (ulong,1)
+SUBNC (ulong,2)
+SUBNC (ulong,3)
+SUBNC (ulong,4)
+SUBNC (ulong,6)
+SUBNC (ulong,8)
+