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