[committed] Use more logicals to eliminate useless test/compare instructions

Jeff Law jeffreyalaw@gmail.com
Wed Jun 23 14:20:48 GMT 2021


Nothing too special here, this allows using generic logicals to 
eliminate redundant test/compare instructions.

We need to pass in the INSN to the support routines so that it can be 
examined to see if CC is relevant after this insn.  If the condition 
codes are relevant, then we can't use the various sub-word sequences 
that are used when the 2nd source is a constant with limited bits 
set/clear.  We need that for both the output template and the length 
calculation.  These cases don't happen often and even when they do we 
still win, particularly for SImode.

There is one case I'm aware of where we could use a special sequence and 
have valid condition codes.  An IOR with a constant operand with at 
least 0xffff8000 set in the mask.  In this case we an IOR the low word 
and use extrs to propagate the bit into the high word.  But while it 
does set ZN in the expected way, they're both compile time constants in 
this case (0,1 respectively) so it's not particularly useful to try and 
express the CC status.

Committed to the trunk.

Jeff

-------------- next part --------------
commit 402c818ac0b19d168e9ffc0b3413344dd6020f6a
Author: Jeff Law <jeffreyalaw@gmail.com>
Date:   Tue Jun 22 15:25:11 2021 -0400

    Use more logicals to eliminate useless test/compare instructions
    
    gcc/
            * config/h8300/logical.md (<code><mode>3<ccnz>): Use <cczn>
            so this pattern can be used for test/compare removal.  Pass
            current insn to compute_logical_op_length and output_logical_op.
            * config/h8300/h8300.c (compute_logical_op_cc): Remove.
            (h8300_and_costs): Add argument to compute_logical_op_length.
            (output_logical_op): Add new argument.  Use it to determine if the
            condition codes are used and adjust the output accordingly.
            (compute_logical_op_length): Add new argument and update length
            computations when condition codes are used.
            * config/h8300/h8300-protos.h (compute_logical_op_length): Update
            prototype.
            (output_logical_op): Likewise.

diff --git a/gcc/config/h8300/h8300-protos.h b/gcc/config/h8300/h8300-protos.h
index af653292a9d..d7efa978aa0 100644
--- a/gcc/config/h8300/h8300-protos.h
+++ b/gcc/config/h8300/h8300-protos.h
@@ -36,10 +36,11 @@ extern const char *output_simode_bld (int, rtx[]);
 extern void final_prescan_insn (rtx_insn *, rtx *, int);
 extern int h8300_expand_movsi (rtx[]);
 extern machine_mode  h8300_select_cc_mode (RTX_CODE, rtx, rtx);
-extern const char *output_logical_op (machine_mode, rtx_code code, rtx *);
-extern unsigned int compute_logical_op_length (machine_mode, rtx_code, rtx *);
+extern const char *output_logical_op (machine_mode, rtx_code code,
+				      rtx *, rtx_insn *);
+extern unsigned int compute_logical_op_length (machine_mode, rtx_code,
+					      rtx *, rtx_insn *);
 
-extern int compute_logical_op_cc (machine_mode, rtx *);
 extern int compute_a_shift_cc (rtx, rtx *);
 #ifdef HAVE_ATTR_cc
 extern enum attr_cc compute_plussi_cc (rtx *);
diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c
index 2b88325d2f7..511c2b28e40 100644
--- a/gcc/config/h8300/h8300.c
+++ b/gcc/config/h8300/h8300.c
@@ -1100,7 +1100,7 @@ h8300_and_costs (rtx x)
   operands[1] = XEXP (x, 0);
   operands[2] = XEXP (x, 1);
   operands[3] = x;
-  return compute_logical_op_length (GET_MODE (x), AND, operands) / 2;
+  return compute_logical_op_length (GET_MODE (x), AND, operands, NULL) / 2;
 }
 
 /* Compute the cost of a shift insn.  */
@@ -2881,7 +2881,7 @@ compute_plussi_cc (rtx *operands)
 /* Output a logical insn.  */
 
 const char *
-output_logical_op (machine_mode mode, rtx_code code, rtx *operands)
+output_logical_op (machine_mode mode, rtx_code code, rtx *operands, rtx_insn *insn)
 {
   /* Pretend that every byte is affected if both operands are registers.  */
   const unsigned HOST_WIDE_INT intval =
@@ -2906,6 +2906,19 @@ output_logical_op (machine_mode mode, rtx_code code, rtx *operands)
   const char *opname;
   char insn_buf[100];
 
+  /* INSN is the current insn, we examine its overall form to see if we're
+     supposed to set or clobber the condition codes.
+
+     This is important to know.  If we are setting condition codes, then we
+     must do the operation in MODE and not in some smaller size.
+
+     The key is to look at the second object in the PARALLEL. If it is not
+     a CLOBBER, then we care about the condition codes.  */
+  rtx pattern = PATTERN (insn);
+  gcc_assert (GET_CODE (pattern) == PARALLEL);
+  rtx second_op = XVECEXP (pattern, 0, 1);
+  bool cc_meaningful = (GET_CODE (second_op) != CLOBBER);
+
   switch (code)
     {
     case AND:
@@ -2928,8 +2941,9 @@ output_logical_op (machine_mode mode, rtx_code code, rtx *operands)
       output_asm_insn (insn_buf, operands);
       break;
     case E_HImode:
-      /* First, see if we can finish with one insn.  */
-      if (b0 != 0 && b1 != 0)
+      /* First, see if we can (or must) finish with one insn.  */
+      if (cc_meaningful
+	  || (b0 != 0 && b1 != 0))
 	{
 	  sprintf (insn_buf, "%s.w\t%%T2,%%T0", opname);
 	  output_asm_insn (insn_buf, operands);
@@ -2964,10 +2978,11 @@ output_logical_op (machine_mode mode, rtx_code code, rtx *operands)
 
       /* Check if doing everything with one insn is no worse than
 	 using multiple insns.  */
-      if (w0 != 0 && w1 != 0
-	  && !(lower_half_easy_p && upper_half_easy_p)
-	  && !(code == IOR && w1 == 0xffff
-	       && (w0 & 0x8000) != 0 && lower_half_easy_p))
+      if (cc_meaningful
+	  || (w0 != 0 && w1 != 0
+	      && !(lower_half_easy_p && upper_half_easy_p)
+	      && !(code == IOR && w1 == 0xffff
+		   && (w0 & 0x8000) != 0 && lower_half_easy_p)))
 	{
 	  sprintf (insn_buf, "%s.l\t%%S2,%%S0", opname);
 	  output_asm_insn (insn_buf, operands);
@@ -3037,7 +3052,7 @@ output_logical_op (machine_mode mode, rtx_code code, rtx *operands)
 /* Compute the length of a logical insn.  */
 
 unsigned int
-compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands)
+compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands, rtx_insn *insn)
 {
   /* Pretend that every byte is affected if both operands are registers.  */
   const unsigned HOST_WIDE_INT intval =
@@ -3061,6 +3076,23 @@ compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands)
   /* Insn length.  */
   unsigned int length = 0;
 
+  /* INSN is the current insn, we examine its overall form to see if we're
+     supposed to set or clobber the condition codes.
+
+     This is important to know.  If we are setting condition codes, then we
+     must do the operation in MODE and not in some smaller size.
+
+     The key is to look at the second object in the PARALLEL. If it is not
+     a CLOBBER, then we care about the condition codes.  */
+  bool cc_meaningful = false;
+  if (insn)
+    {
+      rtx pattern = PATTERN (insn);
+      gcc_assert (GET_CODE (pattern) == PARALLEL);
+      rtx second_op = XVECEXP (pattern, 0, 1);
+      cc_meaningful = (GET_CODE (second_op) != CLOBBER);
+    }
+
   switch (mode)
     {
     case E_QImode:
@@ -3068,7 +3100,8 @@ compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands)
 
     case E_HImode:
       /* First, see if we can finish with one insn.  */
-      if (b0 != 0 && b1 != 0)
+      if (cc_meaningful
+	  || (b0 != 0 && b1 != 0))
 	{
 	  length = h8300_length_from_table (operands[1], operands[2],
 					    &logicw_length_table);
@@ -3098,10 +3131,11 @@ compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands)
 
       /* Check if doing everything with one insn is no worse than
 	 using multiple insns.  */
-      if (w0 != 0 && w1 != 0
-	  && !(lower_half_easy_p && upper_half_easy_p)
-	  && !(code == IOR && w1 == 0xffff
-	       && (w0 & 0x8000) != 0 && lower_half_easy_p))
+      if (cc_meaningful
+	  || (w0 != 0 && w1 != 0
+	      && !(lower_half_easy_p && upper_half_easy_p)
+	      && !(code == IOR && w1 == 0xffff
+		   && (w0 & 0x8000) != 0 && lower_half_easy_p)))
 	{
 	  length = h8300_length_from_table (operands[1], operands[2],
 					    &logicl_length_table);
@@ -3158,80 +3192,6 @@ compute_logical_op_length (machine_mode mode, rtx_code code, rtx *operands)
   return length;
 }
 
-/* Compute which flag bits are valid after a logical insn.  */
-
-int
-compute_logical_op_cc (machine_mode mode, rtx *operands)
-{
-  /* Figure out the logical op that we need to perform.  */
-  enum rtx_code code = GET_CODE (operands[3]);
-  /* Pretend that every byte is affected if both operands are registers.  */
-  const unsigned HOST_WIDE_INT intval =
-    (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
-			      /* Always use the full instruction if the
-				 first operand is in memory.  It is better
-				 to use define_splits to generate the shorter
-				 sequence where valid.  */
-			      && register_operand (operands[1], VOIDmode)
-			      ? INTVAL (operands[2]) : 0x55555555);
-  /* The determinant of the algorithm.  If we perform an AND, 0
-     affects a bit.  Otherwise, 1 affects a bit.  */
-  const unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
-  /* Break up DET into pieces.  */
-  const unsigned HOST_WIDE_INT b0 = (det >>  0) & 0xff;
-  const unsigned HOST_WIDE_INT b1 = (det >>  8) & 0xff;
-  const unsigned HOST_WIDE_INT w0 = (det >>  0) & 0xffff;
-  const unsigned HOST_WIDE_INT w1 = (det >> 16) & 0xffff;
-  int lower_half_easy_p = 0;
-  int upper_half_easy_p = 0;
-  /* Condition code.  */
-  enum attr_old_cc cc = OLD_CC_CLOBBER;
-
-  switch (mode)
-    {
-    case E_HImode:
-      /* First, see if we can finish with one insn.  */
-      if (b0 != 0 && b1 != 0)
-	{
-	  cc = OLD_CC_SET_ZNV;
-	}
-      break;
-    case E_SImode:
-      /* Determine if the lower half can be taken care of in no more
-	 than two bytes.  */
-      lower_half_easy_p = (b0 == 0
-			   || b1 == 0
-			   || (code != IOR && w0 == 0xffff));
-
-      /* Determine if the upper half can be taken care of in no more
-         than two bytes.  */
-      upper_half_easy_p = ((code != IOR && w1 == 0xffff)
-			   || (code == AND && w1 == 0xff00));
-
-      /* Check if doing everything with one insn is no worse than
-	 using multiple insns.  */
-      if (w0 != 0 && w1 != 0
-	  && !(lower_half_easy_p && upper_half_easy_p)
-	  && !(code == IOR && w1 == 0xffff
-	       && (w0 & 0x8000) != 0 && lower_half_easy_p))
-	{
-	  cc = OLD_CC_SET_ZNV;
-	}
-      else
-	{
-	  if (code == IOR
-	      && w1 == 0xffff
-	      && (w0 & 0x8000) != 0)
-	    {
-	      cc = OLD_CC_SET_ZNV;
-	    }
-	}
-      break;
-    default:
-      gcc_unreachable ();
-    }
-  return cc;
-}
 

 #if 0
 /* Expand a conditional branch.  */
diff --git a/gcc/config/h8300/logical.md b/gcc/config/h8300/logical.md
index 07d36cf0ef4..f07c79e1eac 100644
--- a/gcc/config/h8300/logical.md
+++ b/gcc/config/h8300/logical.md
@@ -251,17 +251,16 @@
 		   (logicals:QHSI (match_dup 1) (match_dup 2)))
 	      (clobber (reg:CC CC_REG))])])
 
-(define_insn "*<code><mode>3_clobber_flags"
+(define_insn "*<code><mode>3<cczn>"
   [(set (match_operand:QHSI 0 "h8300_dst_operand" "=rQ")
 	(logicals:QHSI
 	  (match_operand:QHSI 1 "h8300_dst_operand" "%0")
 	  (match_operand:QHSI 2 "h8300_src_operand" "rQi")))
    (clobber (reg:CC CC_REG))]
   "h8300_operands_match_p (operands)"
-  { return output_logical_op (<MODE>mode, <CODE>, operands); }
+  { return output_logical_op (<MODE>mode, <CODE>, operands, insn); }
   [(set (attr "length")
-	(symbol_ref "compute_logical_op_length (<MODE>mode, <CODE>, operands)"))])
-
+	(symbol_ref "compute_logical_op_length (<MODE>mode, <CODE>, operands, insn)"))])
 

 ;; ----------------------------------------------------------------------
 ;; NOT INSTRUCTIONS


More information about the Gcc-patches mailing list