This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Committed] ifcvt.c solution to PR9814


After two months without any objections, I've decided to commit my
patch to implement the missed optimization described in bugzilla PR
tree-optimization/9814.

http://gcc.gnu.org/ml/gcc-patches/2005-03/msg01956.html

I agree with Kazu's comment that these transformations should also
be performed at the tree-ssa level, but in the mean-time there's no
reason to continue to generate poor code for bit-field manipulations
if we have a reasonable patch.  As such I propose to leave this PR
open, as the "tree-optimization" aspect hasn't been resolved, and
just unassign myself.

The following patch is just a freshened version of the one posted
previously.  However, for good measure I've no added an execution
test to trigger these code-paths and ensure we don't generate
incorrect code.

The following patch has been tested on i686-pc-linux-gnu with a
full "make bootstrap", all default languages, and regression tested
with a top-level "make -k check" with no new failures.  I've confirmed
that we correctly optimize the functions in the test case, and that
before this patch mainline generates conditional jumps (just in case
somebody had managed to catch these cases in tree-ssa already).

Committed to mainline CVS.



2005-05-26  Roger Sayle  <roger@eyesopen.com>

	PR tree-optimization/9814
	* ifcvt.c (noce_emit_move_insn): If we fail to recognize the move
	instruction, add the necessary clobbers by re-expanding the RTL
	for arithmetic operations via optab.c's expand_unop/expand_binop.
	(noce_try_bitop): New function to optimize bit manipulation idioms
	of the form "if (x & C) x = x op C" and "if (!(x & C) x = x op C".
	(noce_process_if_block): Call noce_try_bitop.

	* gcc.dg/pr9814-1.c: New test case.


Index: ifcvt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ifcvt.c,v
retrieving revision 1.187
diff -c -3 -p -r1.187 ifcvt.c
*** ifcvt.c	19 May 2005 06:29:45 -0000	1.187
--- ifcvt.c	26 May 2005 19:18:38 -0000
*************** noce_emit_move_insn (rtx x, rtx y)
*** 687,693 ****

    if (GET_CODE (x) != STRICT_LOW_PART)
      {
!       emit_move_insn (x, y);
        return;
      }

--- 687,743 ----

    if (GET_CODE (x) != STRICT_LOW_PART)
      {
!       rtx seq, insn, target;
!       optab ot;
!
!       start_sequence ();
!       insn = emit_move_insn (x, y);
!       seq = get_insns ();
!       end_sequence();
!
!       if (recog_memoized (insn) <= 0)
! 	switch (GET_RTX_CLASS (GET_CODE (y)))
! 	  {
! 	  case RTX_UNARY:
! 	    ot = code_to_optab[GET_CODE (y)];
! 	    if (ot)
! 	      {
! 		start_sequence ();
! 		target = expand_unop (GET_MODE (y), ot, XEXP (y, 0), x, 0);
! 		if (target != NULL_RTX)
! 		  {
! 		    if (target != x)
! 		      emit_move_insn (x, target);
! 		    seq = get_insns ();
! 		  }
! 		end_sequence ();
! 	      }
! 	    break;
!
! 	  case RTX_BIN_ARITH:
! 	  case RTX_COMM_ARITH:
! 	    ot = code_to_optab[GET_CODE (y)];
! 	    if (ot)
! 	      {
! 		start_sequence ();
! 		target = expand_binop (GET_MODE (y), ot,
! 				       XEXP (y, 0), XEXP (y, 1),
! 				       x, 0, OPTAB_DIRECT);
! 		if (target != NULL_RTX)
! 		  {
! 		    if (target != x)
! 		      emit_move_insn (x, target);
! 		    seq = get_insns ();
! 		  }
! 		end_sequence ();
! 	      }
! 	    break;
!
! 	  default:
! 	    break;
! 	  }
!
!       emit_insn (seq);
        return;
      }

*************** noce_try_sign_mask (struct noce_if_info
*** 1815,1820 ****
--- 1865,1969 ----
  }


+ /* Optimize away "if (x & C) x |= C" and similar bit manipulation
+    transformations.  */
+
+ static int
+ noce_try_bitop (struct noce_if_info *if_info)
+ {
+   rtx cond, x, a, result, seq;
+   enum machine_mode mode;
+   enum rtx_code code;
+   int bitnum;
+
+   x = if_info->x;
+   cond = if_info->cond;
+   code = GET_CODE (cond);
+
+   /* Check for no else condition.  */
+   if (! rtx_equal_p (x, if_info->b))
+     return FALSE;
+
+   /* Check for a suitable condition.  */
+   if (code != NE && code != EQ)
+     return FALSE;
+   if (XEXP (cond, 1) != const0_rtx)
+     return FALSE;
+   cond = XEXP (cond, 0);
+
+   /* ??? We could also handle AND here.  */
+   if (GET_CODE (cond) == ZERO_EXTRACT)
+     {
+       if (XEXP (cond, 1) != const1_rtx
+ 	  || GET_CODE (XEXP (cond, 2)) != CONST_INT
+ 	  || ! rtx_equal_p (x, XEXP (cond, 0)))
+ 	return FALSE;
+       bitnum = INTVAL (XEXP (cond, 2));
+       mode = GET_MODE (x);
+       if (bitnum >= HOST_BITS_PER_WIDE_INT)
+ 	return FALSE;
+     }
+   else
+     return FALSE;
+
+   a = if_info->a;
+   if (GET_CODE (a) == IOR || GET_CODE (a) == XOR)
+     {
+       /* Check for "if (X & C) x = x op C".  */
+       if (! rtx_equal_p (x, XEXP (a, 0))
+           || GET_CODE (XEXP (a, 1)) != CONST_INT
+ 	  || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
+ 	     != (unsigned HOST_WIDE_INT) 1 << bitnum)
+         return FALSE;
+
+       /* if ((x & C) == 0) x |= C; is transformed to x |= C.   */
+       /* if ((x & C) != 0) x |= C; is transformed to nothing.  */
+       if (GET_CODE (a) == IOR)
+ 	result = (code == NE) ? a : NULL_RTX;
+       else if (code == NE)
+ 	{
+ 	  /* if ((x & C) == 0) x ^= C; is transformed to x |= C.   */
+ 	  result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode);
+ 	  result = simplify_gen_binary (IOR, mode, x, result);
+ 	}
+       else
+ 	{
+ 	  /* if ((x & C) != 0) x ^= C; is transformed to x &= ~C.  */
+ 	  result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode);
+ 	  result = simplify_gen_binary (AND, mode, x, result);
+ 	}
+     }
+   else if (GET_CODE (a) == AND)
+     {
+       /* Check for "if (X & C) x &= ~C".  */
+       if (! rtx_equal_p (x, XEXP (a, 0))
+ 	  || GET_CODE (XEXP (a, 1)) != CONST_INT
+ 	  || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
+ 	     != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode)))
+         return FALSE;
+
+       /* if ((x & C) == 0) x &= ~C; is transformed to nothing.  */
+       /* if ((x & C) != 0) x &= ~C; is transformed to x &= ~C.  */
+       result = (code == EQ) ? a : NULL_RTX;
+     }
+   else
+     return FALSE;
+
+   if (result)
+     {
+       start_sequence ();
+       noce_emit_move_insn (x, result);
+       seq = end_ifcvt_sequence (if_info);
+       if (!seq)
+ 	return FALSE;
+
+       emit_insn_before_setloc (seq, if_info->jump,
+ 			       INSN_LOCATOR (if_info->insn_a));
+     }
+   return TRUE;
+ }
+
+
  /* Similar to get_condition, only the resulting condition must be
     valid at JUMP, instead of at EARLIEST.  */

*************** noce_process_if_block (struct ce_if_bloc
*** 2078,2083 ****
--- 2227,2234 ----
      goto success;
    if (noce_try_store_flag (&if_info))
      goto success;
+   if (noce_try_bitop (&if_info))
+     goto success;
    if (noce_try_minmax (&if_info))
      goto success;
    if (noce_try_abs (&if_info))


/* PR tree-optimization/9814  */
/* { dg-do run } */
/* { dg-options "-O2" } */

extern void abort(void);

int test1(int x)
{
  if (x & 2)
    x |= 2;
  return x;
}

int test2(int x)
{
  if (!(x & 2))
    x |= 2;
  return x;
}

int test3(int x)
{
  if (x & 2)
    x ^= 2;
  return x;
}

int test4(int x)
{
  if (!(x & 2))
    x ^= 2;
  return x;
}

int test5(int x)
{
  if (x & 2)
    x &= ~2;
  return x;
}

int test6(int x)
{
  if (!(x & 2))
    x &= ~2;
  return x;
}

int main()
{
  if (test1(0) != 0)
    abort();
  if (test1(2) != 2)
    abort();
  if (test1(5) != 5)
    abort();
  if (test1(7) != 7)
    abort();

  if (test2(0) != 2)
    abort();
  if (test2(2) != 2)
    abort();
  if (test2(5) != 7)
    abort();
  if (test2(7) != 7)
    abort();

  if (test3(0) != 0)
    abort();
  if (test3(2) != 0)
    abort();
  if (test3(5) != 5)
    abort();
  if (test3(7) != 5)
    abort();

  if (test4(0) != 2)
    abort();
  if (test4(2) != 2)
    abort();
  if (test4(5) != 7)
    abort();
  if (test4(7) != 7)
    abort();

  if (test5(0) != 0)
    abort();
  if (test5(2) != 0)
    abort();
  if (test5(5) != 5)
    abort();
  if (test5(7) != 5)
    abort();

  if (test6(0) != 0)
    abort();
  if (test6(2) != 2)
    abort();
  if (test6(5) != 5)
    abort();
  if (test6(7) != 7)
    abort();

  return 0;
}


Roger
--


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]