diff mbox

[AVR] : Fix PR50447 (3/n)

Message ID 4E7CAAC3.2070209@gjlay.de
State New
Headers show

Commit Message

Georg-Johann Lay Sept. 23, 2011, 3:50 p.m. UTC
This is the compare part to fix PR50447.

Just like the preceding changes to fix PR50447 it's a micro-optimization to
smarter print-out of instructions.

The patch covers comparisons against HI and SI integers.

The byte-wide comparison allows reusing the value in the scratch register and
individual treatment of zero.  Moreover, ADIW can be used in some rare situations.

And there is a bit of code clean-up.

Tested without regressions.

Ok to commit?

Johann

	PR target/50447
	* config/avr/avr.md (adjust_len): Add alternatives "tsthi",
	"tstsi", "compare".
	(*cmpqi_sign_extend): Use s8_operand.
	(*cmphi, *cmpsi): Rewrite using avr_out_compare.
	* config/avr/avr-protos.h (compare_diff_p, compare_eq_p): Remove
	prototypes.
	(out_tsthi, out_tstsi): Remove prototypes.
	(avr_out_tsthi, avr_out_tstsi): New prototypes.
	* config/avr/avr.c (out_tsthi, out_tstsi): Remove functions.
	(avr_asm_len): Negative length now sets *plen to -length.
	(compare_sign_p): Return bool instead of int.
	(compare_diff_p, compare_eq_p): Ditto and make static.
	(avr_out_tsthi): New function.
	(avr_out_tstsi): New function.
	(avr_out_compare): New function.
	(adjust_insn_length): Handle ADJUST_LEN_TSTHI, ADJUST_LEN_TSTSI,
	ADJUST_LEN_COMPARE.

Comments

Denis Chertykov Sept. 23, 2011, 4:44 p.m. UTC | #1
2011/9/23 Georg-Johann Lay <avr@gjlay.de>:
> This is the compare part to fix PR50447.
>
> Just like the preceding changes to fix PR50447 it's a micro-optimization to
> smarter print-out of instructions.
>
> The patch covers comparisons against HI and SI integers.
>
> The byte-wide comparison allows reusing the value in the scratch register and
> individual treatment of zero.  Moreover, ADIW can be used in some rare situations.
>
> And there is a bit of code clean-up.
>
> Tested without regressions.
>
> Ok to commit?
>
> Johann
>
>        PR target/50447
>        * config/avr/avr.md (adjust_len): Add alternatives "tsthi",
>        "tstsi", "compare".
>        (*cmpqi_sign_extend): Use s8_operand.
>        (*cmphi, *cmpsi): Rewrite using avr_out_compare.
>        * config/avr/avr-protos.h (compare_diff_p, compare_eq_p): Remove
>        prototypes.
>        (out_tsthi, out_tstsi): Remove prototypes.
>        (avr_out_tsthi, avr_out_tstsi): New prototypes.
>        * config/avr/avr.c (out_tsthi, out_tstsi): Remove functions.
>        (avr_asm_len): Negative length now sets *plen to -length.
>        (compare_sign_p): Return bool instead of int.
>        (compare_diff_p, compare_eq_p): Ditto and make static.
>        (avr_out_tsthi): New function.
>        (avr_out_tstsi): New function.
>        (avr_out_compare): New function.
>        (adjust_insn_length): Handle ADJUST_LEN_TSTHI, ADJUST_LEN_TSTSI,
>        ADJUST_LEN_COMPARE.
>

Please commit it with previous patch.

Denis.
diff mbox

Patch

Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 179116)
+++ config/avr/avr.md	(working copy)
@@ -136,7 +136,7 @@  (define_attr "length" ""
 ;; Otherwise do special processing depending on the attribute.
 
 (define_attr "adjust_len"
-  "yes,no,reload_in32,out_bitop"
+  "yes,no,reload_in32,out_bitop,tsthi,tstsi,compare"
   (const_string "yes"))
 
 ;; Define mode iterators
@@ -3328,125 +3328,62 @@  (define_insn "*cmpqi"
 
 (define_insn "*cmpqi_sign_extend"
   [(set (cc0)
-        (compare (sign_extend:HI
-		  (match_operand:QI 0 "register_operand"  "d"))
-		 (match_operand:HI 1 "const_int_operand" "n")))]
-  "INTVAL (operands[1]) >= -128 && INTVAL (operands[1]) <= 127"
+        (compare (sign_extend:HI (match_operand:QI 0 "register_operand" "d"))
+                 (match_operand:HI 1 "s8_operand"                       "n")))]
+  ""
   "cpi %0,lo8(%1)"
   [(set_attr "cc" "compare")
    (set_attr "length" "1")])
 
 (define_insn "*cmphi"
   [(set (cc0)
-	(compare (match_operand:HI 0 "register_operand"  "!w,r,r,d,d,r,r")
-		 (match_operand:HI 1 "nonmemory_operand" "L,L,r,M,i,M,i")))
-   (clobber (match_scratch:QI 2 "=X,X,X,X,&d,&d,&d"))]
+        (compare (match_operand:HI 0 "register_operand"  "!w,r,r,d ,r  ,d,r")
+                 (match_operand:HI 1 "nonmemory_operand" "L ,L,r,s ,s  ,M,n")))
+   (clobber (match_scratch:QI 2                         "=X ,X,X,&d,&d ,X,&d"))]
   ""
-  "*{
-  switch (which_alternative)
-    {
-    case 0: case 1:
-      return out_tsthi (insn, operands[0], NULL);
-
-    case 2:
-      return (AS2 (cp,%A0,%A1) CR_TAB
-              AS2 (cpc,%B0,%B1));
-    case 3:
-      if (reg_unused_after (insn, operands[0])
-          && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 63
-          && test_hard_reg_class (ADDW_REGS, operands[0]))
-        return AS2 (sbiw,%0,%1);
-       else
-        return (AS2 (cpi,%0,%1) CR_TAB
-                AS2 (cpc,%B0,__zero_reg__));
-    case 4:
-      if (reg_unused_after (insn, operands[0]))
-        return (AS2 (subi,%0,lo8(%1))  CR_TAB
-                AS2 (sbci,%B0,hi8(%1)));
-      else
-        return (AS2 (ldi, %2,hi8(%1))  CR_TAB
-	        AS2 (cpi, %A0,lo8(%1)) CR_TAB
-	        AS2 (cpc, %B0,%2));
-   case 5:
-      return (AS2 (ldi, %2,lo8(%1))  CR_TAB
-	      AS2 (cp, %A0,%2) CR_TAB
-	      AS2 (cpc, %B0,__zero_reg__));
-
-   case 6:
-      return (AS2 (ldi, %2,lo8(%1))  CR_TAB
-              AS2 (cp, %A0,%2)       CR_TAB
-              AS2 (ldi, %2,hi8(%1)) CR_TAB
-	      AS2 (cpc, %B0,%2));
-    }
-  return \"bug\";
-}" 
-  [(set_attr "cc" "compare,compare,compare,compare,compare,compare,compare")
-   (set_attr "length" "1,2,2,2,3,3,4")])
+  {
+    switch (which_alternative)
+      {
+      case 0:
+      case 1:
+        return avr_out_tsthi (insn, operands, NULL);
+        
+      case 2:
+        return "cp %A0,%A1\;cpc %B0,%B1";
+
+      case 3:
+        return reg_unused_after (insn, operands[0])
+               ? "subi %A0,lo8(%1)\;sbci %B0,hi8(%1)"
+               : "ldi %2,hi8(%1)\;cpi %A0,lo8(%1)\;cpc %B0,%2";
+               
+      case 4:
+        return "ldi %2,lo8(%1)\;cp %A0,%2\;ldi %2,hi8(%1)\;cpc %B0,%2";
+      }
+      
+    return avr_out_compare (insn, operands, NULL);
+  } 
+  [(set_attr "cc" "compare")
+   (set_attr "length" "1,2,2,3,4,2,4")
+   (set_attr "adjust_len" "tsthi,tsthi,no,no,no,compare,compare")])
 
 
 (define_insn "*cmpsi"
   [(set (cc0)
-	(compare (match_operand:SI 0 "register_operand"  "r,r,d,d,r,r")
-		 (match_operand:SI 1 "nonmemory_operand" "L,r,M,i,M,i")))
-   (clobber (match_scratch:QI 2 "=X,X,X,&d,&d,&d"))]
+        (compare (match_operand:SI 0 "register_operand"  "r,r ,d,r ,r")
+                 (match_operand:SI 1 "nonmemory_operand" "L,r ,M,M ,n")))
+   (clobber (match_scratch:QI 2                         "=X,X ,X,&d,&d"))]
   ""
-  "*{
-  switch (which_alternative)
-    {
-    case 0:
-      return out_tstsi (insn, operands[0], NULL);
-
-    case 1:
-      return (AS2 (cp,%A0,%A1) CR_TAB
-              AS2 (cpc,%B0,%B1) CR_TAB
-	      AS2 (cpc,%C0,%C1) CR_TAB
-	      AS2 (cpc,%D0,%D1));
-    case 2:
-      if (reg_unused_after (insn, operands[0])
-          && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 63
-          && test_hard_reg_class (ADDW_REGS, operands[0]))
-        return (AS2 (sbiw,%0,%1) CR_TAB
-                AS2 (cpc,%C0,__zero_reg__) CR_TAB
-                AS2 (cpc,%D0,__zero_reg__));
-      else
-        return (AS2 (cpi,%A0,lo8(%1))  CR_TAB
-                AS2 (cpc,%B0,__zero_reg__) CR_TAB
-                AS2 (cpc,%C0,__zero_reg__) CR_TAB
-                AS2 (cpc,%D0,__zero_reg__));
-    case 3:
-      if (reg_unused_after (insn, operands[0]))
-        return (AS2 (subi,%A0,lo8(%1))  CR_TAB
-                AS2 (sbci,%B0,hi8(%1))  CR_TAB
-                AS2 (sbci,%C0,hlo8(%1))  CR_TAB
-                AS2 (sbci,%D0,hhi8(%1)));
-      else
-       return (AS2 (cpi, %A0,lo8(%1))   CR_TAB
-	       AS2 (ldi, %2,hi8(%1))  CR_TAB
-	       AS2 (cpc, %B0,%2)       CR_TAB
-	       AS2 (ldi, %2,hlo8(%1))  CR_TAB
-	       AS2 (cpc, %C0,%2)       CR_TAB
-	       AS2 (ldi, %2,hhi8(%1)) CR_TAB
-	       AS2 (cpc, %D0,%2));
-    case 4:
-        return (AS2 (ldi,%2,lo8(%1))        CR_TAB
-                AS2 (cp,%A0,%2)            CR_TAB
-                AS2 (cpc,%B0,__zero_reg__) CR_TAB
-                AS2 (cpc,%C0,__zero_reg__) CR_TAB
-                AS2 (cpc,%D0,__zero_reg__));
-    case 5:
-       return (AS2 (ldi, %2,lo8(%1))   CR_TAB
-               AS2 (cp, %A0,%2)        CR_TAB
-	       AS2 (ldi, %2,hi8(%1))  CR_TAB
-	       AS2 (cpc, %B0,%2)       CR_TAB
-	       AS2 (ldi, %2,hlo8(%1))  CR_TAB
-	       AS2 (cpc, %C0,%2)       CR_TAB
-	       AS2 (ldi, %2,hhi8(%1)) CR_TAB
-	       AS2 (cpc, %D0,%2));
-    }
-  return \"bug\";
-}"
-  [(set_attr "cc" "compare,compare,compare,compare,compare,compare")
-   (set_attr "length" "4,4,4,7,5,8")])
+  {
+    if (0 == which_alternative)
+      return avr_out_tstsi (insn, operands, NULL);
+    else if (1 == which_alternative)
+      return "cp %A0,%A1\;cpc %B0,%B1\;cpc %C0,%C1\;cpc %D0,%D1";
+      
+    return avr_out_compare (insn, operands, NULL);
+  }
+  [(set_attr "cc" "compare")
+   (set_attr "length" "4,4,4,5,8")
+   (set_attr "adjust_len" "tstsi,no,compare,compare,compare")])
 
 
 ;; ----------------------------------------------------------------------
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 179115)
+++ config/avr/avr-protos.h	(working copy)
@@ -47,7 +47,6 @@  extern void init_cumulative_args (CUMULA
 
 #ifdef RTX_CODE
 extern void asm_output_external_libcall (FILE *file, rtx symref);
-extern int compare_diff_p (rtx insn);
 extern const char *output_movqi (rtx insn, rtx operands[], int *l);
 extern const char *output_movhi (rtx insn, rtx operands[], int *l);
 extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l);
@@ -57,8 +56,9 @@  extern const char *out_movhi_mr_r (rtx i
 extern const char *out_movsi_r_mr (rtx insn, rtx op[], int *l);
 extern const char *out_movsi_mr_r (rtx insn, rtx op[], int *l);
 extern const char *output_movsisf (rtx insn, rtx operands[], int *l);
-extern const char *out_tstsi (rtx insn, rtx src, int *l);
-extern const char *out_tsthi (rtx insn, rtx src, int *l);
+extern const char *avr_out_tstsi (rtx, rtx*, int*);
+extern const char *avr_out_tsthi (rtx, rtx*, int*);
+extern const char *avr_out_compare (rtx, rtx*, int*);
 extern const char *ret_cond_branch (rtx x, int len, int reverse);
 
 extern const char *ashlqi3_out (rtx insn, rtx operands[], int *len);
@@ -102,7 +102,6 @@  extern void final_prescan_insn (rtx insn
 extern int avr_simplify_comparison_p (enum machine_mode mode,
 				      RTX_CODE op, rtx x);
 extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
-extern int compare_eq_p (rtx insn);
 extern void out_shift_with_cnt (const char *templ, rtx insn,
 				rtx operands[], int *len, int t_len);
 extern rtx avr_incoming_return_addr_rtx (void);
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 179115)
+++ config/avr/avr.c	(working copy)
@@ -69,9 +69,7 @@  static const char *ptrreg_to_str (int);
 static const char *cond_string (enum rtx_code);
 static int avr_num_arg_regs (enum machine_mode, const_tree);
 
-static RTX_CODE compare_condition (rtx insn);
 static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
-static int compare_sign_p (rtx insn);
 static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *);
 static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
 static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *);
@@ -1291,7 +1289,8 @@  avr_legitimize_address (rtx x, rtx oldx,
        by OPERANDS.  This is just forwarding to output_asm_insn.
    
    If PLEN != NULL:
-       Add N_WORDS to *PLEN.
+       If N_WORDS >= 0  Add N_WORDS to *PLEN.
+       If N_WORDS < 0   Set *PLEN to -N_WORDS.
        Don't output anything.
 */
 
@@ -1304,7 +1303,10 @@  avr_asm_len (const char* tpl, rtx* opera
     }
   else
     {
-      *plen += n_words;
+      if (n_words < 0)
+        *plen = -n_words;
+      else
+        *plen += n_words;
     }
 }
 
@@ -3052,28 +3054,30 @@  compare_condition (rtx insn)
   return UNKNOWN;
 }
 
-/* Returns nonzero if INSN is a tst insn that only tests the sign.  */
 
-static int
+/* Returns true iff INSN is a tst insn that only tests the sign.  */
+
+static bool
 compare_sign_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
   return (cond == GE || cond == LT);
 }
 
-/* Returns nonzero if the next insn is a JUMP_INSN with a condition
+
+/* Returns true iff the next insn is a JUMP_INSN with a condition
    that needs to be swapped (GT, GTU, LE, LEU).  */
 
-int
+static bool
 compare_diff_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
   return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0;
 }
 
-/* Returns nonzero if INSN is a compare insn with the EQ or NE condition.  */
+/* Returns true iff INSN is a compare insn with the EQ or NE condition.  */
 
-int
+static bool
 compare_eq_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
@@ -3081,56 +3085,173 @@  compare_eq_p (rtx insn)
 }
 
 
+/* Output compare instruction
+
+      compare (XOP[0], XOP[1])
+
+   for an HI/SI register XOP[0] and an integer XOP[1].  Return "".
+   XOP[2] is an 8-bit scratch register as needed.
+
+   PLEN == NULL:  Output instructions.
+   PLEN != NULL:  Set *PLEN to the length (in words) of the sequence.
+                  Don't output anything.  */
+
+const char*
+avr_out_compare (rtx insn, rtx *xop, int *plen)
+{
+  /* Register to compare and value to compare against. */
+  rtx xreg = xop[0];
+  rtx xval = xop[1];
+  
+  /* MODE of the comparison.  */
+  enum machine_mode mode = GET_MODE (xreg);
+
+  /* Number of bytes to operate on.  */
+  int i, n_bytes = GET_MODE_SIZE (mode);
+
+  /* Value (0..0xff) held in clobber register xop[2] or -1 if unknown.  */
+  int clobber_val = -1;
+
+  gcc_assert (REG_P (xreg)
+              && CONST_INT_P (xval));
+  
+  if (plen)
+    *plen = 0;
+
+  for (i = 0; i < n_bytes; i++)
+    {
+      /* We compare byte-wise.  */
+      rtx reg8 = simplify_gen_subreg (QImode, xreg, mode, i);
+      rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+      /* 8-bit value to compare with this byte.  */
+      unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+      /* Registers R16..R31 can operate with immediate.  */
+      bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+      xop[0] = reg8;
+      xop[1] = gen_int_mode (val8, QImode);
+
+      /* Word registers >= R24 can use SBIW/ADIW with 0..63.  */
+
+      if (i == 0
+          && test_hard_reg_class (ADDW_REGS, reg8))
+        {
+          int val16 = trunc_int_for_mode (INTVAL (xval), HImode);
+          
+          if (IN_RANGE (val16, 0, 63)
+              && (val8 == 0
+                  || reg_unused_after (insn, xreg)))
+            {
+              avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+              i++;
+              continue;
+            }
+
+          if (n_bytes == 2
+              && IN_RANGE (val16, -63, -1)
+              && compare_eq_p (insn)
+              && reg_unused_after (insn, xreg))
+            {
+              avr_asm_len ("adiw %0,%n1", xop, plen, 1);
+              break;
+            }
+        }
+
+      /* Comparing against 0 is easy.  */
+      
+      if (val8 == 0)
+        {
+          avr_asm_len (i == 0
+                       ? "cp %0,__zero_reg__"
+                       : "cpc %0,__zero_reg__", xop, plen, 1);
+          continue;
+        }
+
+      /* Upper registers can compare and subtract-with-carry immediates.
+         Notice that compare instructions do the same as respective subtract
+         instruction; the only difference is that comparisons don't write
+         the result back to the target register.  */
+
+      if (ld_reg_p)
+        {
+          if (i == 0)
+            {
+              avr_asm_len ("cpi %0,%1", xop, plen, 1);
+              continue;
+            }
+          else if (reg_unused_after (insn, xreg))
+            {
+              avr_asm_len ("sbci %0,%1", xop, plen, 1);
+              continue;
+            }
+        }
+
+      /* Must load the value into the scratch register.  */
+
+      gcc_assert (REG_P (xop[2]));
+              
+      if (clobber_val != (int) val8)
+        avr_asm_len ("ldi %2,%1", xop, plen, 1);
+      clobber_val = (int) val8;
+              
+      avr_asm_len (i == 0
+                   ? "cp %0,%2"
+                   : "cpc %0,%2", xop, plen, 1);
+    }
+
+  return "";
+}
+
+
 /* Output test instruction for HImode.  */
 
-const char *
-out_tsthi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tsthi (rtx insn, rtx *op, int *plen)
 {
   if (compare_sign_p (insn))
     {
-      if (l) *l = 1;
-      return AS1 (tst,%B0);
+      avr_asm_len ("tst %B0", op, plen, -1);
     }
-  if (reg_unused_after (insn, op)
-      && compare_eq_p (insn))
+  else if (reg_unused_after (insn, op[0])
+           && compare_eq_p (insn))
     {
       /* Faster than sbiw if we can clobber the operand.  */
-      if (l) *l = 1;
-      return "or %A0,%B0";
+      avr_asm_len ("or %A0,%B0", op, plen, -1);
     }
-  if (test_hard_reg_class (ADDW_REGS, op))
+  else
     {
-      if (l) *l = 1;
-      return AS2 (sbiw,%0,0);
+      avr_out_compare (insn, op, plen);
     }
-  if (l) *l = 2;
-  return (AS2 (cp,%A0,__zero_reg__) CR_TAB
-          AS2 (cpc,%B0,__zero_reg__));
+
+  return "";
 }
 
 
 /* Output test instruction for SImode.  */
 
-const char *
-out_tstsi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tstsi (rtx insn, rtx *op, int *plen)
 {
   if (compare_sign_p (insn))
     {
-      if (l) *l = 1;
-      return AS1 (tst,%D0);
+      avr_asm_len ("tst %D0", op, plen, -1);
     }
-  if (test_hard_reg_class (ADDW_REGS, op))
+  else if (reg_unused_after (insn, op[0])
+           && compare_eq_p (insn))
     {
-      if (l) *l = 3;
-      return (AS2 (sbiw,%A0,0) CR_TAB
-              AS2 (cpc,%C0,__zero_reg__) CR_TAB
-              AS2 (cpc,%D0,__zero_reg__));
-    }
-  if (l) *l = 4;
-  return (AS2 (cp,%A0,__zero_reg__) CR_TAB
-          AS2 (cpc,%B0,__zero_reg__) CR_TAB
-          AS2 (cpc,%C0,__zero_reg__) CR_TAB
-          AS2 (cpc,%D0,__zero_reg__));
+      /* Faster than sbiw if we can clobber the operand.  */
+      avr_asm_len ("or %A0,%B0" CR_TAB
+                   "or %A0,%C0" CR_TAB
+                   "or %A0,%D0", op, plen, -3);
+    }
+  else
+    {
+      avr_out_compare (insn, op, plen);
+    }
+
+  return "";
 }
 
 
@@ -4851,6 +4972,10 @@  adjust_insn_length (rtx insn, int len)
           avr_out_bitop (insn, op, &len);
           break;
 
+        case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
+        case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
+        case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break;
+          
         default:
           gcc_unreachable();
         }
@@ -4886,15 +5011,6 @@  adjust_insn_length (rtx insn, int len)
 	      break;
 	    }
 	}
-      else if (op[0] == cc0_rtx && REG_P (op[1]))
-	{
-	  switch (GET_MODE (op[1]))
-	    {
-	    case HImode: out_tsthi (insn, op[1], &len); break;
-	    case SImode: out_tstsi (insn, op[1], &len); break;
-	    default: break;
-	    }
-	}
     }
   set = single_set (insn);
   if (set)