Fix for __builtin_expect on PowerPC

Michael Meissner meissner@tiktok.cygnus.com
Wed Jun 20 00:16:00 GMT 2001


On Mon, Jun 18, 2001 at 11:54:45PM -0400, Michael Meissner wrote:
> On Mon, Jun 18, 2001 at 03:02:26PM +0100, Graham Stott wrote:
> > Micheal
> > 
> > I don't see a prototype for the new expand_builtin_expect_jump function.
> 
> Whoops, you are right.  I had a prototype in rtl.h with my first set of
> changes, which also moved the PROB* macros from predict.c there.  But when I
> synched with the recent changes in the tree, I decided to use the functions in
> predict.c to create the note, and dropped moving the PROB* macros, and forgot
> about the prototype.

Here is the final patch I checked in:

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

	* builtins.c (predict.h): Include.
	(expand_builtin_expect): Update comment.
	(expand_builtin_expect_jump): New function to expand
	__builtin_expect inside of a conditional jump expansion.

	* expr.c (do_jump): Special case __builtin_expect (<test>, 0) and
	__builtin_expect (<test>, 1).

	* Makefile.in (builtins.o): Depend on $(PREDICT_H).

	* rtl.h (expand_builtin_expect_jump): Add prototype.

*** builtins.c.~1~	Sat Jun 16 02:35:22 2001
--- 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
*** expr.c.~1~	Sat Jun 16 02:35:29 2001
--- expr.c	Mon Jun 18 23:58:25 2001
*************** emit_block_move (x, y, size, align)
*** 1773,1778 ****
--- 1773,1779 ----
  	  DECL_EXTERNAL (fn) = 1;
  	  TREE_PUBLIC (fn) = 1;
  	  DECL_ARTIFICIAL (fn) = 1;
+ 	  TREE_NOTHROW (fn) = 1;
  	  make_decl_rtl (fn, NULL);
  	  assemble_external (fn);
  	}
*************** clear_storage (object, size, align)
*** 2659,2665 ****
  
  	     For targets where libcalls and normal calls have different
  	     conventions for returning pointers, we could end up generating
! 	      incorrect code.
  
  	     So instead of using a libcall sequence we build up a suitable
  	     CALL_EXPR and expand the call in the normal fashion.  */
--- 2660,2666 ----
  
  	     For targets where libcalls and normal calls have different
  	     conventions for returning pointers, we could end up generating
! 	     incorrect code.
  
  	     So instead of using a libcall sequence we build up a suitable
  	     CALL_EXPR and expand the call in the normal fashion.  */
*************** clear_storage (object, size, align)
*** 2677,2682 ****
--- 2678,2684 ----
  	      DECL_EXTERNAL (fn) = 1;
  	      TREE_PUBLIC (fn) = 1;
  	      DECL_ARTIFICIAL (fn) = 1;
+ 	      TREE_NOTHROW (fn) = 1;
  	      make_decl_rtl (fn, NULL);
  	      assemble_external (fn);
  	    }
*************** store_constructor (exp, target, align, c
*** 4547,4553 ****
  
        /* If the constructor has fewer fields than the structure
  	 or if we are initializing the structure to mostly zeros,
! 	 clear the whole structure first.  Don't do this is TARGET is
  	 register whose mode size isn't equal to SIZE since clear_storage
  	 can't handle this case.  */
        else if (size > 0
--- 4549,4555 ----
  
        /* If the constructor has fewer fields than the structure
  	 or if we are initializing the structure to mostly zeros,
! 	 clear the whole structure first.  Don't do this if TARGET is a
  	 register whose mode size isn't equal to SIZE since clear_storage
  	 can't handle this case.  */
        else if (size > 0
*************** expand_expr (exp, target, tmode, modifie
*** 7034,7047 ****
  	    if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
  		|| GET_CODE (op0) == CONCAT || GET_CODE (op0) == ADDRESSOF)
  	      {
! 		tree nt = build_qualified_type (TREE_TYPE (tem),
! 						(TYPE_QUALS (TREE_TYPE (tem))
! 						 | TYPE_QUAL_CONST));
! 		rtx memloc = assign_temp (nt, 1, 1, 1);
  
! 		mark_temp_addr_taken (memloc);
! 		emit_move_insn (memloc, op0);
! 		op0 = memloc;
  	      }
  
  	    if (GET_CODE (op0) != MEM)
--- 7036,7057 ----
  	    if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
  		|| GET_CODE (op0) == CONCAT || GET_CODE (op0) == ADDRESSOF)
  	      {
! 		/* If the operand is a SAVE_EXPR, we can deal with this by
! 		   forcing the SAVE_EXPR into memory.  */
! 		if (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR)
! 		  put_var_into_stack (TREE_OPERAND (exp, 0));
! 		else
! 		  {
! 		    tree nt
! 		      = build_qualified_type (TREE_TYPE (tem),
! 					      (TYPE_QUALS (TREE_TYPE (tem))
! 					       | TYPE_QUAL_CONST));
! 		    rtx memloc = assign_temp (nt, 1, 1, 1);
  
! 		    mark_temp_addr_taken (memloc);
! 		    emit_move_insn (memloc, op0);
! 		    op0 = memloc;
! 		  }
  	      }
  
  	    if (GET_CODE (op0) != MEM)
*************** do_jump (exp, if_false_label, if_true_la
*** 9902,9907 ****
--- 9912,9950 ----
  	  }
        }
        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:
*** Makefile.in.~1~	Thu Jun 14 00:43:01 2001
--- Makefile.in	Wed Jun 20 01:13:25 2001
*************** C_OBJS = c-parse.o c-lang.o $(C_AND_OBJC
*** 731,737 ****
  
  OBJS =									\
   alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o		\
!  combine.o conflict.o convert.o cse.o cselib.o dbxout.o dce.o		\
   dependence.o diagnostic.o doloop.o dominance.o dwarf2asm.o dwarf2out.o	\
   dwarfout.o emit-rtl.o except.o explow.o expmed.o expr.o final.o flow.o	\
   fold-const.o function.o gcse.o genrtl.o ggc-common.o global.o graph.o	\
--- 731,737 ----
  
  OBJS =									\
   alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o		\
!  combine.o conflict.o convert.o cse.o cselib.o dbxout.o ssa-dce.o	\
   dependence.o diagnostic.o doloop.o dominance.o dwarf2asm.o dwarf2out.o	\
   dwarfout.o emit-rtl.o except.o explow.o expmed.o expr.o final.o flow.o	\
   fold-const.o function.o gcse.o genrtl.o ggc-common.o global.o graph.o	\
*************** gcc.o: gcc.c $(CONFIG_H) $(SYSTEM_H) int
*** 1263,1268 ****
--- 1263,1274 ----
    -c $(srcdir)/gcc.c)
  
  gccspec.o: gccspec.c $(CONFIG_H) $(SYSTEM_H) $(GCC_H)
+ 	(SHLIB_LINK='$(SHLIB_LINK)' \
+ 	SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
+ 	$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+   $(DRIVER_DEFINES) \
+   -c $(srcdir)/gccspec.c)
+ 
  cppspec.o: cppspec.c $(CONFIG_H) $(SYSTEM_H) $(GCC_H)
  
  tree-check.h: s-check ; @true
*************** 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  \
--- 1371,1377 ----
  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  \
*************** lcm.o : lcm.c $(CONFIG_H) $(SYSTEM_H) $(
*** 1430,1436 ****
  ssa.o : ssa.c $(CONFIG_H) $(SYSTEM_H) $(REGS_H) varray.h $(EXPR_H) \
     hard-reg-set.h flags.h function.h real.h insn-config.h $(RECOG_H)	\
     $(BASIC_BLOCK_H) output.h ssa.h
! dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) \
     ssa.h insn-config.h $(RECOG_H) output.h
  conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \
     $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
--- 1436,1442 ----
  ssa.o : ssa.c $(CONFIG_H) $(SYSTEM_H) $(REGS_H) varray.h $(EXPR_H) \
     hard-reg-set.h flags.h function.h real.h insn-config.h $(RECOG_H)	\
     $(BASIC_BLOCK_H) output.h ssa.h
! ssa-dce.o : ssa-dce.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) \
     ssa.h insn-config.h $(RECOG_H) output.h
  conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \
     $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
*************** doloop.o : doloop.c $(CONFIG_H) $(SYSTEM
*** 1444,1450 ****
     $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TM_P_H) toplev.h
  unroll.o : unroll.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h function.h \
     $(INTEGRATE_H) $(REGS_H) $(RECOG_H) flags.h $(EXPR_H) $(LOOP_H) toplev.h \
!    hard-reg-set.h varray.h $(BASIC_BLOCK_H) $(TM_P_H)
  flow.o : flow.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h insn-config.h \
     $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
     function.h except.h $(EXPR_H) ssa.h $(GGC_H) $(TM_P_H)
--- 1450,1456 ----
     $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TM_P_H) toplev.h
  unroll.o : unroll.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h function.h \
     $(INTEGRATE_H) $(REGS_H) $(RECOG_H) flags.h $(EXPR_H) $(LOOP_H) toplev.h \
!    hard-reg-set.h varray.h $(BASIC_BLOCK_H) $(TM_P_H) $(PREDICT_H)
  flow.o : flow.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h insn-config.h \
     $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
     function.h except.h $(EXPR_H) ssa.h $(GGC_H) $(TM_P_H)
*** rtl.h.~1~	Sat Jun 16 02:35:31 2001
--- rtl.h	Mon Jun 18 23:51:49 2001
*************** extern int ceil_log2			PARAMS ((unsigned
*** 1146,1151 ****
--- 1146,1154 ----
  #define plus_constant_for_output(X,C)  \
    plus_constant_for_output_wide (X, (HOST_WIDE_INT) (C))
  
+ /* In builtins.c */
+ extern rtx expand_builtin_expect_jump	PARAMS ((union tree_node *, rtx, rtx));
+ 
  /* In explow.c */
  extern void set_stack_check_libfunc PARAMS ((rtx));
  extern HOST_WIDE_INT trunc_int_for_mode	PARAMS ((HOST_WIDE_INT,

-- 
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



More information about the Gcc-patches mailing list