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]

Fix for __builtin_expect on PowerPC


The following code fixes __builtin_expect (a == b, 1) on PowerPC's
(and probably a host of other machines) by optimizing __builtin_expect
as we are creating the jump, rather than emitting the flag of the
expected value and then trying to optimize it.  On the PowerPC, the
compiler would generate code equivalent to:

	tmp = a == b;
	if (__builtin_expect (tmp, 1)) ...

and for some tests, it would generate the SCC via a branch, and then
do the real branch after that.

I checked various __builtin_expect code sequences on the x86 and
PowerPC, and it generates the same or better code.  I also
bootstrapped and compared the x86 compiler.

Any comments before I check it in?

2001-06-16  Michael Meissner  <meissner@redhat.com>

	* builtins.c (predict.h): Include.
	(expand_builtin_expect): Update comment.
	(expand_builtin_expect_jump): New function to expand a
	__builtin_expect in the context of a conditional jump, to fix slow
	code on machines like the PowerPC.

	* expr.c (do_jump): If we are doing a conditional jump on a
	__builtin_expect value, call expand_builtin_expect_jump.

	* Makefile.in (builtins.o): Depend on predict.h.

*** gcc/builtins.c.~1~	Sat Jun 16 02:35:22 2001
--- gcc/builtins.c	Sat Jun 16 12:23:18 2001
*************** Boston, MA 02111-1307, USA.  */
*** 36,41 ****
--- 36,42 ----
  #include "output.h"
  #include "typeclass.h"
  #include "toplev.h"
+ #include "predict.h"
  #include "tm_p.h"
  
  #define CALLED_AS_BUILT_IN(NODE) \
*************** expand_builtin_fputs (arglist, ignore)
*** 3232,3239 ****
  		      VOIDmode, EXPAND_NORMAL);
  }
  
! /* Expand a call to __builtin_expect.  We return our argument and
!    emit a NOTE_INSN_EXPECTED_VALUE note.  */
  
  static rtx
  expand_builtin_expect (arglist, target)
--- 3233,3241 ----
  		      VOIDmode, EXPAND_NORMAL);
  }
  
! /* Expand a call to __builtin_expect.  We return our argument and emit a
!    NOTE_INSN_EXPECTED_VALUE note.  This is the expansion of __builtin_expect in
!    a non-jump context.  */
  
  static rtx
  expand_builtin_expect (arglist, target)
*************** expand_builtin_expect (arglist, target)
*** 3273,3278 ****
--- 3275,3389 ----
  
    return target;
  }
+ 
+ /* Like expand_builtin_expect, except do this in a jump context.  This is
+    called from do_jump if the conditional is a __builtin_expect.  Return either
+    a SEQUENCE of insns to emit the jump or NULL if we cannot optimize
+    __builtin_expect.  We need to optimize this at jump time so that machines
+    like the PowerPC don't turn the test into a SCC operation, and then jump
+    based on the test being 0/1.  */
+ 
+ rtx
+ expand_builtin_expect_jump (exp, if_false_label, if_true_label)
+      tree exp;
+      rtx if_false_label;
+      rtx if_true_label;
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree arg0 = TREE_VALUE (arglist);
+   tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+   rtx ret = NULL_RTX;
+ 
+   /* Only handle __builtin_expect (test, 0) and
+      __builtin_expect (test, 1).  */
+   if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE
+       && TREE_CODE (arg1) == INTEGER_CST
+       && (TREE_INT_CST_LOW (arg1) == 0 || TREE_INT_CST_LOW (arg1) == 1)
+       && TREE_INT_CST_HIGH (arg1) == 0)
+     {
+       int j;
+       int num_jumps = 0;
+ 
+       /* Expand the jump insns.  */
+       start_sequence ();
+       do_jump (arg0, if_false_label, if_true_label);
+       ret = gen_sequence ();
+       end_sequence ();
+ 
+       /* Now that the __builtin_expect has been validated, go through and add
+ 	 the expect's to each of the conditional jumps.  If we run into an
+ 	 error, just give up and generate the 'safe' code of doing a SCC
+ 	 operation and then doing a branch on that.  */
+       for (j = 0; j < XVECLEN (ret, 0); j++)
+ 	{
+ 	  rtx insn = XVECEXP (ret, 0, j);
+ 	  rtx pattern;
+ 
+ 	  if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)
+ 	      && (pattern = pc_set (insn)) != NULL_RTX)
+ 	    {
+ 	      rtx ifelse = SET_SRC (pattern);
+ 	      rtx label;
+ 	      int taken;
+ 
+ 	      if (GET_CODE (ifelse) != IF_THEN_ELSE)
+ 		continue;
+ 
+ 	      if (GET_CODE (XEXP (ifelse, 1)) == LABEL_REF)
+ 		{
+ 		  taken = 1;
+ 		  label = XEXP (XEXP (ifelse, 1), 0);
+ 		}
+ 	      /* An inverted jump reverses the probabilities.  */
+ 	      else if (GET_CODE (XEXP (ifelse, 2)) == LABEL_REF)
+ 		{
+ 		  taken = 0;
+ 		  label = XEXP (XEXP (ifelse, 2), 0);
+ 		}
+ 	      /* We shouldn't have to worry about conditional returns during
+ 		 the expansion stage, but handle it gracefully anyway.  */
+ 	      else if (GET_CODE (XEXP (ifelse, 1)) == RETURN)
+ 		{
+ 		  taken = 1;
+ 		  label = NULL_RTX;
+ 		}
+ 	      /* An inverted return reverses the probabilities.  */
+ 	      else if (GET_CODE (XEXP (ifelse, 2)) == RETURN)
+ 		{
+ 		  taken = 0;
+ 		  label = NULL_RTX;
+ 		}
+ 	      else
+ 		continue;
+ 
+ 	      /* If the test is expected to fail, reverse the
+ 		 probabilities.  */
+ 	      if (TREE_INT_CST_LOW (arg1) == 0)
+ 		taken = 1 - taken;
+ 
+ 	      /* If we are jumping to the false label, reverse the
+ 		 probabilities.  */
+ 	      if (label == NULL_RTX)
+ 		;		/* conditional return */
+ 	      else if (label == if_false_label)
+ 		taken = 1 - taken;
+ 	      else if (label != if_true_label)
+ 		continue;
+ 
+ 	      num_jumps++;
+ 	      predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken);
+ 	    }
+ 	}
+ 
+       /* If no jumps were modified, fail and do __builtin_expect the normal
+ 	 way.  */
+       if (num_jumps == 0)
+ 	ret = NULL_RTX;
+     }
+ 
+   return ret;
+ }
+ 
  
  /* Expand an expression EXP that calls a built-in function,
     with result going to TARGET if that's convenient
*** gcc/expr.c.~1~	Sat Jun 16 02:35:29 2001
--- gcc/expr.c	Sat Jun 16 12:51:25 2001
*************** do_jump (exp, if_false_label, if_true_la
*** 9903,9908 ****
--- 9903,9941 ----
        }
        break;
  
+       /* Special case:
+ 		__builtin_expect (<test>, 0)	and
+ 		__builtin_expect (<test>, 1)
+ 
+ 	 We need to do this here, so that <test> is not converted to a SCC
+ 	 operation on machines that use condition code registers and COMPARE
+ 	 like the PowerPC, and then the jump is done based on whether the SCC
+ 	 operation produced a 1 or 0.  */
+     case CALL_EXPR:
+       /* Check for a built-in function.  */
+       if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR)
+ 	{
+ 	  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ 	  tree arglist = TREE_OPERAND (exp, 1);
+ 
+ 	  if (TREE_CODE (fndecl) == FUNCTION_DECL
+ 	      && DECL_BUILT_IN (fndecl)
+ 	      && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT
+ 	      && arglist != NULL_TREE
+ 	      && TREE_CHAIN (arglist) != NULL_TREE)
+ 	    {
+ 	      rtx seq = expand_builtin_expect_jump (exp, if_false_label,
+ 						    if_true_label);
+ 
+ 	      if (seq != NULL_RTX)
+ 		{
+ 		  emit_insn (seq);
+ 		  return;
+ 		}
+ 	    }
+ 	}
+       /* fall through and generate the normal code.  */
+ 
      default:
      normal:
        temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
*** gcc/Makefile.in.~1~	Thu Jun 14 00:43:01 2001
--- gcc/Makefile.in	Sat Jun 16 15:30:24 2001
*************** expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) 
*** 1365,1371 ****
  builtins.o : builtins.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
     function.h $(REGS_H) $(EXPR_H) insn-config.h \
     $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
!    except.h $(TM_P_H)
  calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h $(EXPR_H) \
     $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H)
  expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h  \
--- 1365,1371 ----
  builtins.o : builtins.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
     function.h $(REGS_H) $(EXPR_H) insn-config.h \
     $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
!    except.h $(TM_P_H) predict.h
  calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h $(EXPR_H) \
     $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H)
  expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h  \

-- 
Michael Meissner, Red Hat, Inc.  (GCC group)
PMB 198, 174 Littleton Road #3, Westford, Massachusetts 01886, USA
Work:	  meissner@redhat.com		phone: +1 978-486-9304
Non-work: meissner@spectacle-pond.org	fax:   +1 978-692-4482


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