Commit 89cfe6e5 authored by rth's avatar rth
Browse files

* builtins.c (expand_builtin_expect): New.

        (expand_builtin): Call it.
        * builtins.def (BUILT_IN_EXPECT): New.
        * c-common.c (c_common_nodes_and_builtins): Declare __builtin_expect.
        * extend.texi: Document it.

        * predict.c (expected_value_to_br_prob): New.
        (find_expected_value): New.
        * basic-block.h (expected_value_to_br_prob): Declare.
        * toplev.c (rest_of_compilation): Invoke it.

        * rtl.h (NOTE_EXPECTED_VALUE): New.
        (NOTE_INSN_EXPECTED_VALUE): New.
        * rtl.c (note_insn_name): Update.
        * print-rtl.c (print_rtx): Reorg NOTE_LINE_NUMBER special
        cases; handle NOTE_INSN_EXPECTED_VALUE.


git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@33211 138bc75d-0d04-0410-961f-82ee72b054a4
parent f3619338
2000-04-17 Richard Henderson <rth@cygnus.com>
* builtins.c (expand_builtin_expect): New.
(expand_builtin): Call it.
* builtins.def (BUILT_IN_EXPECT): New.
* c-common.c (c_common_nodes_and_builtins): Declare __builtin_expect.
* extend.texi: Document it.
* predict.c (expected_value_to_br_prob): New.
(find_expected_value): New.
* basic-block.h (expected_value_to_br_prob): Declare.
* toplev.c (rest_of_compilation): Invoke it.
* rtl.h (NOTE_EXPECTED_VALUE): New.
(NOTE_INSN_EXPECTED_VALUE): New.
* rtl.c (note_insn_name): Update.
* print-rtl.c (print_rtx): Reorg NOTE_LINE_NUMBER special
cases; handle NOTE_INSN_EXPECTED_VALUE.
2000-04-17 Jakub Jelinek <jakub@redhat.com>
 
* config/sparc/sparc.c (eligible_for_sibcall_delay): Cannot use
......
......@@ -449,6 +449,7 @@ extern rtx emit_block_insn_before PARAMS ((rtx, rtx, basic_block));
/* In predict.c */
extern void estimate_probability PARAMS ((struct loops *));
extern void expected_value_to_br_prob PARAMS ((void));
/* In flow.c */
extern void reorder_basic_blocks PARAMS ((void));
......
......@@ -103,6 +103,7 @@ static rtx expand_builtin_alloca PARAMS ((tree, rtx));
static rtx expand_builtin_ffs PARAMS ((tree, rtx, rtx));
static rtx expand_builtin_frame_address PARAMS ((tree));
static tree stabilize_va_list PARAMS ((tree, int));
static rtx expand_builtin_expect PARAMS ((tree, rtx));
/* Return the alignment in bits of EXP, a pointer valued expression.
But don't return more than MAX_ALIGN no matter what.
......@@ -2306,6 +2307,48 @@ expand_builtin_ffs (arglist, target, subtarget)
abort ();
return target;
}
/* 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)
tree arglist;
rtx target;
{
tree exp, c;
rtx note, rtx_c;
if (arglist == NULL_TREE
|| TREE_CHAIN (arglist) == NULL_TREE)
return const0_rtx;
exp = TREE_VALUE (arglist);
c = TREE_VALUE (TREE_CHAIN (arglist));
if (TREE_CODE (c) != INTEGER_CST)
{
error ("second arg to `__builtin_expect' must be a constant");
c = integer_zero_node;
}
target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL);
/* Don't bother with expected value notes for integral constants. */
if (GET_CODE (target) != CONST_INT)
{
/* We do need to force this into a register so that we can be
moderately sure to be able to correctly interpret the branch
condition later. */
target = force_reg (GET_MODE (target), target);
rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL);
note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c);
}
return target;
}
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
......@@ -2581,6 +2624,8 @@ expand_builtin (exp, target, subtarget, mode, ignore)
return expand_builtin_va_end (arglist);
case BUILT_IN_VA_COPY:
return expand_builtin_va_copy (arglist);
case BUILT_IN_EXPECT:
return expand_builtin_expect (arglist, target);
default: /* just do library call, if unknown builtin */
error ("built-in function `%s' not currently supported",
......
......@@ -79,6 +79,7 @@ DEF_BUILTIN(BUILT_IN_VARARGS_START)
DEF_BUILTIN(BUILT_IN_STDARG_START)
DEF_BUILTIN(BUILT_IN_VA_END)
DEF_BUILTIN(BUILT_IN_VA_COPY)
DEF_BUILTIN(BUILT_IN_EXPECT)
/* C++ extensions */
DEF_BUILTIN(BUILT_IN_NEW)
......
......@@ -3767,6 +3767,16 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins)
endlink))),
BUILT_IN_VA_COPY, BUILT_IN_NORMAL, NULL_PTR);
/* ??? Ought to be `T __builtin_expect(T, T)' for any type T. */
builtin_function ("__builtin_expect",
build_function_type (long_integer_type_node,
tree_cons (NULL_TREE,
long_integer_type_node,
tree_cons (NULL_TREE,
long_integer_type_node,
endlink))),
BUILT_IN_EXPECT, BUILT_IN_NORMAL, NULL_PTR);
/* Currently under experimentation. */
builtin_function ("__builtin_memcpy", memcpy_ftype, BUILT_IN_MEMCPY,
BUILT_IN_NORMAL, "memcpy");
......
......@@ -3199,7 +3199,9 @@ correspond to the C library functions @code{abort}, @code{abs},
@code{sinf}, @code{sinl}, @code{sqrt}, @code{sqrtf}, @code{sqrtl},
@code{strcmp}, @code{strcpy}, and @code{strlen}.
@table @code
@findex __builtin_constant_p
@item __builtin_constant_p (@var{exp})
You can use the builtin function @code{__builtin_constant_p} to
determine if a value is known to be constant at compile-time and hence
that GNU CC can perform constant-folding on expressions involving that
......@@ -3228,6 +3230,39 @@ or constructor expression (@pxref{Constructors}) and will not return 1
when you pass a constant numeric value to the inline function unless you
specify the @samp{-O} option.
@findex __builtin_expect
@item __builtin_expect(@var{exp}, @var{c})
You may use @code{__builtin_expect} to provide the compiler with
branch prediction information. In general, you should prefer to
use actual profile feedback for this (@samp{-fprofile-arcs}), as
programmers are notoriously bad at predicting how their programs
actually preform. However, there are applications in which this
data is hard to collect.
The return value is the value of @var{exp}, which should be an
integral expression. The value of @var{c} must be a compile-time
constant. The semantics of the builtin are that it is expected
that @var{exp} == @var{c}. For example:
@smallexample
if (__builtin_expect (x, 0))
foo ();
@end smallexample
@noindent
would indicate that we do not expect to call @code{foo}, since
we expect @code{x} to be zero. Since you are limited to integral
expressions for @var{exp}, you should use constructions such as
@smallexample
if (__builtin_expect (ptr != NULL, 1))
error ();
@end smallexample
@noindent
when testing pointer or floating-point values.
@end table
@node Deprecated Features
@section Deprecated Features
......
......@@ -180,4 +180,91 @@ estimate_probability (loops_info)
REG_NOTES (last_insn));
}
}
/* __builtin_expect dropped tokens into the insn stream describing
expected values of registers. Generate branch probabilities
based off these values. */
static rtx find_expected_value PARAMS ((rtx, rtx));
void
expected_value_to_br_prob ()
{
rtx insn, cond, earliest, ev;
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
{
/* Look for simple conditional branches. */
if (GET_CODE (insn) != JUMP_INSN)
continue;
if (! condjump_p (insn) || simplejump_p (insn))
continue;
/* Collect the branch condition. Some machines can branch on
user values directly, others need a compare instruction. If
the branch condition involves a MODE_INT register, try that
expression first. Otherwise use get_condition. */
cond = XEXP (SET_SRC (PATTERN (insn)), 0);
if (GET_RTX_CLASS (GET_CODE (cond)) != '<')
abort ();
if (GET_CODE (XEXP (cond, 0)) == REG
&& GET_MODE_CLASS (GET_MODE (XEXP (cond, 0))) == MODE_INT
&& (ev = find_expected_value (cond, insn)) != NULL_RTX)
;
else if ((cond = get_condition (insn, &earliest)) == NULL_RTX
|| (ev = find_expected_value (cond, earliest)) == NULL_RTX)
continue;
/* Substitute and simplify. Given that the expression we're
building involves two constants, we should wind up with either
true or false. */
cond = gen_rtx_fmt_ee (GET_CODE (cond), VOIDmode,
XEXP (ev, 1), XEXP (cond, 1));
cond = simplify_rtx (cond);
/* Turn the condition into a scaled branch probability. */
if (cond == const0_rtx)
cond = const1_rtx;
else if (cond == const1_rtx)
cond = GEN_INT (REG_BR_PROB_BASE - 1);
else
abort ();
REG_NOTES (insn) = alloc_EXPR_LIST (REG_BR_PROB, cond, REG_NOTES (insn));
}
}
/* Search backwards for a NOTE_INSN_EXPECTED_VALUE note with a register
that matches the condition. */
static rtx
find_expected_value (cond, earliest)
rtx cond, earliest;
{
rtx insn, reg = XEXP (cond, 0);
int timeout;
/* The condition should be (op (reg) (const_int)), otherwise we
won't be able to intuit anything about it. */
if (GET_CODE (reg) != REG
|| GET_CODE (XEXP (cond, 1)) != CONST_INT
|| GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT)
return NULL_RTX;
/* Assuming the user wrote something like `if (__builtin_expect(...))',
we shouldn't have to search too far. Also stop if we reach a code
label or if REG is modified. */
for (insn = earliest, timeout = 10;
insn && timeout > 0;
insn = PREV_INSN (insn), --timeout)
{
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_EXPECTED_VALUE
&& XEXP (NOTE_EXPECTED_VALUE (insn), 0) == reg)
return NOTE_EXPECTED_VALUE (insn);
if (GET_CODE (insn) == CODE_LABEL || reg_set_p (reg, insn))
break;
}
return NULL_RTX;
}
......@@ -162,18 +162,19 @@ print_rtx (in_rtx)
case '0':
if (i == 3 && GET_CODE (in_rtx) == NOTE)
{
if (NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_EH_REGION_BEG
|| NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_EH_REGION_END)
switch (NOTE_LINE_NUMBER (in_rtx))
{
case NOTE_INSN_EH_REGION_BEG:
case NOTE_INSN_EH_REGION_END:
if (flag_dump_unnumbered)
fprintf (outfile, " #");
else
fprintf (outfile, " %d", NOTE_EH_HANDLER (in_rtx));
sawclose = 1;
}
else if (NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_BLOCK_BEG
|| NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_BLOCK_END)
{
break;
case NOTE_INSN_BLOCK_BEG:
case NOTE_INSN_BLOCK_END:
fprintf (outfile, " ");
if (flag_dump_unnumbered)
fprintf (outfile, "#");
......@@ -181,36 +182,51 @@ print_rtx (in_rtx)
fprintf (outfile, HOST_PTR_PRINTF,
(char *) NOTE_BLOCK (in_rtx));
sawclose = 1;
}
else if (NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_RANGE_START
|| NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_RANGE_END
|| NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_LIVE)
{
break;
case NOTE_INSN_RANGE_START:
case NOTE_INSN_RANGE_END:
case NOTE_INSN_LIVE:
indent += 2;
if (!sawclose)
fprintf (outfile, " ");
print_rtx (NOTE_RANGE_INFO (in_rtx));
indent -= 2;
}
else if (NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_BASIC_BLOCK)
{
basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
break;
if (bb != 0)
fprintf (outfile, " [bb %d]", bb->index);
}
else
{
const char * const str = X0STR (in_rtx, i);
if (str == 0)
fputs (dump_for_graph ? " \\\"\\\"" : " \"\"", outfile);
else
{
if (dump_for_graph)
fprintf (outfile, " (\\\"%s\\\")", str);
else
fprintf (outfile, " (\"%s\")", str);
}
case NOTE_INSN_BASIC_BLOCK:
{
basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
if (bb != 0)
fprintf (outfile, " [bb %d]", bb->index);
break;
}
case NOTE_INSN_EXPECTED_VALUE:
indent += 2;
if (!sawclose)
fprintf (outfile, " ");
print_rtx (NOTE_EXPECTED_VALUE (in_rtx));
indent -= 2;
break;
default:
{
const char * const str = X0STR (in_rtx, i);
if (NOTE_LINE_NUMBER (in_rtx) < 0)
;
else if (str == 0)
fputs (dump_for_graph ? " \\\"\\\"" : " \"\"", outfile);
else
{
if (dump_for_graph)
fprintf (outfile, " (\\\"%s\\\")", str);
else
fprintf (outfile, " (\"%s\")", str);
}
break;
}
}
}
break;
......
......@@ -247,7 +247,7 @@ const char * const note_insn_name[NOTE_INSN_MAX - NOTE_INSN_BIAS] =
"NOTE_INSN_EH_REGION_BEG", "NOTE_INSN_EH_REGION_END",
"NOTE_REPEATED_LINE_NUMBER", "NOTE_INSN_RANGE_START",
"NOTE_INSN_RANGE_END", "NOTE_INSN_LIVE",
"NOTE_INSN_BASIC_BLOCK"
"NOTE_INSN_BASIC_BLOCK", "NOTE_INSN_EXPECTED_VALUE"
};
const char * const reg_note_name[] =
......
......@@ -577,6 +577,7 @@ extern const char * const reg_note_name[];
#define NOTE_RANGE_INFO(INSN) XCEXP(INSN, 3, NOTE)
#define NOTE_LIVE_INFO(INSN) XCEXP(INSN, 3, NOTE)
#define NOTE_BASIC_BLOCK(INSN) XCBBDEF(INSN, 3, NOTE)
#define NOTE_EXPECTED_VALUE(INSN) XCEXP(INSN, 3, NOTE)
/* In a NOTE that is a line number, this is the line number.
Other kinds of NOTEs are identified by negative numbers here. */
......@@ -664,6 +665,10 @@ enum insn_note
/* Record the struct for the following basic block. Uses NOTE_BASIC_BLOCK. */
NOTE_INSN_BASIC_BLOCK,
/* Record the expected value of a register at a location. Uses
NOTE_EXPECTED_VALUE; stored as (eq (reg) (const_int)). */
NOTE_INSN_EXPECTED_VALUE,
NOTE_INSN_MAX
};
......
......@@ -2893,9 +2893,10 @@ rest_of_compilation (decl)
goto exit_rest_of_compilation;
}
init_EXPR_INSN_LIST_cache ();
/* We may have potential sibling or tail recursion sites. Select one
(of possibly multiple) methods of performing the call. */
init_EXPR_INSN_LIST_cache ();
if (flag_optimize_sibling_calls)
optimize_sibling_and_tail_recursive_calls ();
......@@ -2962,6 +2963,10 @@ rest_of_compilation (decl)
of the function. */
TIMEVAR (jump_time,
{
/* Turn NOTE_INSN_EXPECTED_VALUE into REG_BR_PROB. Do this
before jump optimization switches branch directions. */
expected_value_to_br_prob ();
reg_scan (insns, max_reg_num (), 0);
jump_optimize (insns, !JUMP_CROSS_JUMP, !JUMP_NOOP_MOVES,
JUMP_AFTER_REGSCAN);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment