mirror of
https://gcc.gnu.org/git/gcc.git
synced 2025-01-06 11:03:43 +08:00
Makefile.in (LIBGCOV): Add _gcov_interval_profiler, _gcov_pow2_profiler and _gcov_one_value_profiler.
* Makefile.in (LIBGCOV): Add _gcov_interval_profiler, _gcov_pow2_profiler and _gcov_one_value_profiler. (tree-profile.o): Add GGC_H and gt-tree-profile.h dependency. (GTFILES): Add $(srcdir)/tree-profile.c. * gcov-io.h (__gcov_interval_profiler, __gcov_pow2_profiler, __gcov_one_value_profiler): Declare. * gimplify.c (force_gimple_operand): Check whether the statements should be produced in ssa form. (force_gimple_operand_bsi): New function. * libgcov.c (__gcov_interval_profiler, __gcov_pow2_profiler, __gcov_one_value_profiler): New functions. * rtl-profile.c (rtl_gen_pow2_profiler): Only measure whether the profiled value is a power of two or not. * tree-cfg.c (tree_block_ends_with_condjump_p): Handle empty blocks. * tree-flow.h (in_ssa_p): Declare. (force_gimple_operand_bsi): Declare. * tree-into-ssa.c (in_ssa_p): New variable. (rewrite_into_ssa): Set in_ssa_p. * tree-outof-ssa.c (rewrite_out_of_ssa): Set in_ssa_p. * tree-profile.c: Include ggc.h and gt-tree-profile.h. (gcov_type_node, tree_interval_profiler_fn, tree_pow2_profiler_fn, tree_one_value_profiler_fn): New variables. (tree_init_edge_profiler): Initialize the profiler function decls. (tree_gen_edge_profiler): Use global gcov_type_node. (prepare_instrumented_value): New function. (tree_gen_interval_profiler, tree_gen_interval_profiler, tree_gen_one_value_profiler): Call the library functions instead of creating instrumentation code. * tree.c (build_fn_decl): New function. * tree.h (build_fn_decl): Declare. * value-prof.c (rtl_divmod_values_to_profile, rtl_find_values_to_profile, rtl_mod_pow2_value_transform, tree_mod_pow2_value_transform, tree_find_values_to_profile): Do not handle may_be_other and precise values of exponents at pow2 profiler. (tree_mod_subtract_transform): Reflect that value field of histogram has changed meaning. (tree_divmod_values_to_profile): Record the values correctly. (tree_values_to_profile): Update comment. * value-prof.h (struct histogram_value_t): Remove pow2 data. From-SVN: r98555
This commit is contained in:
parent
ebccb65d5a
commit
9885da8e1b
@ -1,3 +1,46 @@
|
||||
2005-04-22 Zdenek Dvorak <dvorakz@suse.cz>
|
||||
|
||||
* Makefile.in (LIBGCOV): Add _gcov_interval_profiler,
|
||||
_gcov_pow2_profiler and _gcov_one_value_profiler.
|
||||
(tree-profile.o): Add GGC_H and gt-tree-profile.h dependency.
|
||||
(GTFILES): Add $(srcdir)/tree-profile.c.
|
||||
* gcov-io.h (__gcov_interval_profiler, __gcov_pow2_profiler,
|
||||
__gcov_one_value_profiler): Declare.
|
||||
* gimplify.c (force_gimple_operand): Check whether the statements
|
||||
should be produced in ssa form.
|
||||
(force_gimple_operand_bsi): New function.
|
||||
* libgcov.c (__gcov_interval_profiler, __gcov_pow2_profiler,
|
||||
__gcov_one_value_profiler): New functions.
|
||||
* rtl-profile.c (rtl_gen_pow2_profiler): Only measure whether
|
||||
the profiled value is a power of two or not.
|
||||
* tree-cfg.c (tree_block_ends_with_condjump_p): Handle empty blocks.
|
||||
* tree-flow.h (in_ssa_p): Declare.
|
||||
(force_gimple_operand_bsi): Declare.
|
||||
* tree-into-ssa.c (in_ssa_p): New variable.
|
||||
(rewrite_into_ssa): Set in_ssa_p.
|
||||
* tree-outof-ssa.c (rewrite_out_of_ssa): Set in_ssa_p.
|
||||
* tree-profile.c: Include ggc.h and gt-tree-profile.h.
|
||||
(gcov_type_node, tree_interval_profiler_fn, tree_pow2_profiler_fn,
|
||||
tree_one_value_profiler_fn): New variables.
|
||||
(tree_init_edge_profiler): Initialize the profiler function decls.
|
||||
(tree_gen_edge_profiler): Use global gcov_type_node.
|
||||
(prepare_instrumented_value): New function.
|
||||
(tree_gen_interval_profiler, tree_gen_interval_profiler,
|
||||
tree_gen_one_value_profiler): Call the library functions instead of
|
||||
creating instrumentation code.
|
||||
* tree.c (build_fn_decl): New function.
|
||||
* tree.h (build_fn_decl): Declare.
|
||||
* value-prof.c (rtl_divmod_values_to_profile,
|
||||
rtl_find_values_to_profile, rtl_mod_pow2_value_transform,
|
||||
tree_mod_pow2_value_transform, tree_find_values_to_profile):
|
||||
Do not handle may_be_other and precise values of exponents at pow2
|
||||
profiler.
|
||||
(tree_mod_subtract_transform): Reflect that value field of
|
||||
histogram has changed meaning.
|
||||
(tree_divmod_values_to_profile): Record the values correctly.
|
||||
(tree_values_to_profile): Update comment.
|
||||
* value-prof.h (struct histogram_value_t): Remove pow2 data.
|
||||
|
||||
2005-04-22 Joseph S. Myers <joseph@codesourcery.com>
|
||||
|
||||
* varasm.c (do_assemble_alias): Return early if TREE_ASM_WRITTEN
|
||||
|
@ -995,7 +995,8 @@ LIB2FUNCS_ST = _eprintf __gcc_bcmp
|
||||
# Defined in libgcov.c, included only in gcov library
|
||||
LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
|
||||
_gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
|
||||
_gcov_execv _gcov_execvp _gcov_execve
|
||||
_gcov_execv _gcov_execvp _gcov_execve \
|
||||
_gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler
|
||||
|
||||
FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \
|
||||
_fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
|
||||
@ -2034,7 +2035,7 @@ profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
tree-profile.o : tree-profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||
$(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) function.h \
|
||||
toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H) $(TREE_H) value-prof.h \
|
||||
tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H)
|
||||
tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) $(GGC_H) gt-tree-profile.h
|
||||
rtl-profile.o : tree-profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||
$(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) function.h \
|
||||
toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H) $(TREE_FLOW_H) value-prof.h $(GGC_H)
|
||||
@ -2466,7 +2467,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \
|
||||
$(srcdir)/c-common.h $(srcdir)/c-tree.h $(srcdir)/reload.h \
|
||||
$(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
|
||||
$(srcdir)/dbxout.c $(srcdir)/dwarf2out.c $(srcdir)/dwarf2asm.c \
|
||||
$(srcdir)/dojump.c \
|
||||
$(srcdir)/dojump.c $(srcdir)/tree-profile.c \
|
||||
$(srcdir)/emit-rtl.c $(srcdir)/except.c $(srcdir)/explow.c $(srcdir)/expr.c \
|
||||
$(srcdir)/function.c \
|
||||
$(srcdir)/gcse.c $(srcdir)/integrate.c $(srcdir)/lists.c $(srcdir)/optabs.c \
|
||||
@ -2500,7 +2501,7 @@ gt-dwarf2out.h gt-reg-stack.h gt-dwarf2asm.h \
|
||||
gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parser.h \
|
||||
gt-c-pragma.h gtype-c.h gt-cfglayout.h \
|
||||
gt-tree-mudflap.h gt-tree-complex.h \
|
||||
gt-tree-eh.h \
|
||||
gt-tree-eh.h gt-tree-profile.h \
|
||||
gt-tree-ssanames.h gt-tree-iterator.h gt-gimplify.h \
|
||||
gt-tree-phinodes.h gt-tree-nested.h \
|
||||
gt-tree-ssa-operands.h gt-tree-ssa-propagate.h \
|
||||
|
@ -443,6 +443,11 @@ extern void __gcov_merge_single (gcov_type *, unsigned);
|
||||
consecutive values. */
|
||||
extern void __gcov_merge_delta (gcov_type *, unsigned);
|
||||
|
||||
/* The profiler functions. */
|
||||
extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
|
||||
extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
|
||||
extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
|
||||
|
||||
#ifndef inhibit_libc
|
||||
/* The wrappers around some library functions.. */
|
||||
extern pid_t __gcov_fork (void);
|
||||
|
@ -4662,7 +4662,7 @@ force_gimple_operand (tree expr, tree *stmts, bool simple, tree var)
|
||||
gimple_test_f = simple ? is_gimple_val : is_gimple_reg_rhs;
|
||||
|
||||
push_gimplify_context ();
|
||||
gimplify_ctxp->into_ssa = true;
|
||||
gimplify_ctxp->into_ssa = in_ssa_p;
|
||||
|
||||
if (var)
|
||||
expr = build (MODIFY_EXPR, TREE_TYPE (var), var, expr);
|
||||
@ -4671,12 +4671,31 @@ force_gimple_operand (tree expr, tree *stmts, bool simple, tree var)
|
||||
gimple_test_f, fb_rvalue);
|
||||
gcc_assert (ret != GS_ERROR);
|
||||
|
||||
for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
|
||||
add_referenced_tmp_var (t);
|
||||
if (referenced_vars)
|
||||
{
|
||||
for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
|
||||
add_referenced_tmp_var (t);
|
||||
}
|
||||
|
||||
pop_gimplify_context (NULL);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/* Invokes force_gimple_operand for EXPR with parameters SIMPLE_P and VAR. If
|
||||
some statements are produced, emits them before BSI. */
|
||||
|
||||
tree
|
||||
force_gimple_operand_bsi (block_stmt_iterator *bsi, tree expr,
|
||||
bool simple_p, tree var)
|
||||
{
|
||||
tree stmts;
|
||||
|
||||
expr = force_gimple_operand (expr, &stmts, simple_p, var);
|
||||
if (stmts)
|
||||
bsi_insert_before (bsi, stmts, BSI_SAME_STMT);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
#include "gt-gimplify.h"
|
||||
|
@ -581,6 +581,67 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
|
||||
}
|
||||
#endif /* L_gcov_merge_delta */
|
||||
|
||||
#ifdef L_gcov_interval_profiler
|
||||
/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
|
||||
corresponding counter in COUNTERS. If the VALUE is above or below
|
||||
the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
|
||||
instead. */
|
||||
|
||||
void
|
||||
__gcov_interval_profiler (gcov_type *counters, gcov_type value,
|
||||
int start, unsigned steps)
|
||||
{
|
||||
gcov_type delta = value - start;
|
||||
if (delta < 0)
|
||||
counters[steps + 1]++;
|
||||
else if (delta >= steps)
|
||||
counters[steps]++;
|
||||
else
|
||||
counters[delta]++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_pow2_profiler
|
||||
/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
|
||||
COUNTERS[0] is incremented. */
|
||||
|
||||
void
|
||||
__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
if (value & (value - 1))
|
||||
counters[0]++;
|
||||
else
|
||||
counters[1]++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_one_value_profiler
|
||||
/* Tries to determine the most common value among its inputs. Checks if the
|
||||
value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
|
||||
is incremented. If this is not the case and COUNTERS[1] is not zero,
|
||||
COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
|
||||
VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
|
||||
function is called more than 50% of the time with one value, this value
|
||||
will be in COUNTERS[0] in the end.
|
||||
|
||||
In any case, COUNTERS[2] is incremented. */
|
||||
|
||||
void
|
||||
__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
if (value == counters[0])
|
||||
counters[1]++;
|
||||
else if (counters[1] == 0)
|
||||
{
|
||||
counters[1] = 1;
|
||||
counters[0] = value;
|
||||
}
|
||||
else
|
||||
counters[1]--;
|
||||
counters[2]++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_fork
|
||||
/* A wrapper for the fork function. Flushes the accumulated profiling data, so
|
||||
that they are not counted twice. */
|
||||
|
@ -172,10 +172,9 @@ rtl_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
rtx mem_ref, tmp, mr, uval;
|
||||
rtx sequence;
|
||||
rtx end_of_code_label = gen_label_rtx ();
|
||||
rtx loop_label = gen_label_rtx ();
|
||||
int per_counter = GCOV_TYPE_SIZE / BITS_PER_UNIT;
|
||||
edge e = split_block (BLOCK_FOR_INSN (value->hvalue.rtl.insn),
|
||||
PREV_INSN (value->hvalue.rtl.insn));
|
||||
PREV_INSN (value->hvalue.rtl.insn));
|
||||
|
||||
start_sequence ();
|
||||
|
||||
@ -191,33 +190,20 @@ rtl_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
emit_move_insn (uval, copy_rtx (value->hvalue.rtl.value));
|
||||
|
||||
/* Check for non-power of 2. */
|
||||
if (value->hdata.pow2.may_be_other)
|
||||
{
|
||||
do_compare_rtx_and_jump (copy_rtx (uval), const0_rtx, LE, 0, value->hvalue.rtl.mode,
|
||||
NULL_RTX, NULL_RTX, end_of_code_label);
|
||||
tmp = expand_simple_binop (value->hvalue.rtl.mode, PLUS, copy_rtx (uval),
|
||||
constm1_rtx, NULL_RTX, 0, OPTAB_WIDEN);
|
||||
tmp = expand_simple_binop (value->hvalue.rtl.mode, AND, copy_rtx (uval), tmp,
|
||||
NULL_RTX, 0, OPTAB_WIDEN);
|
||||
do_compare_rtx_and_jump (tmp, const0_rtx, NE, 0, value->hvalue.rtl.mode, NULL_RTX,
|
||||
NULL_RTX, end_of_code_label);
|
||||
}
|
||||
do_compare_rtx_and_jump (copy_rtx (uval), const0_rtx, LE, 0, value->hvalue.rtl.mode,
|
||||
NULL_RTX, NULL_RTX, end_of_code_label);
|
||||
tmp = expand_simple_binop (value->hvalue.rtl.mode, PLUS, copy_rtx (uval),
|
||||
constm1_rtx, NULL_RTX, 0, OPTAB_WIDEN);
|
||||
tmp = expand_simple_binop (value->hvalue.rtl.mode, AND, copy_rtx (uval), tmp,
|
||||
NULL_RTX, 0, OPTAB_WIDEN);
|
||||
do_compare_rtx_and_jump (tmp, const0_rtx, NE, 0, value->hvalue.rtl.mode, NULL_RTX,
|
||||
NULL_RTX, end_of_code_label);
|
||||
|
||||
/* Count log_2(value). */
|
||||
emit_label (loop_label);
|
||||
|
||||
tmp = expand_simple_binop (Pmode, PLUS, copy_rtx (mr), GEN_INT (per_counter), mr, 0, OPTAB_WIDEN);
|
||||
tmp = expand_simple_binop (Pmode, PLUS, copy_rtx (mr), GEN_INT (per_counter),
|
||||
mr, 0, OPTAB_WIDEN);
|
||||
if (tmp != mr)
|
||||
emit_move_insn (copy_rtx (mr), tmp);
|
||||
|
||||
tmp = expand_simple_binop (value->hvalue.rtl.mode, ASHIFTRT, copy_rtx (uval), const1_rtx,
|
||||
uval, 0, OPTAB_WIDEN);
|
||||
if (tmp != uval)
|
||||
emit_move_insn (copy_rtx (uval), tmp);
|
||||
|
||||
do_compare_rtx_and_jump (copy_rtx (uval), const0_rtx, NE, 0, value->hvalue.rtl.mode,
|
||||
NULL_RTX, NULL_RTX, loop_label);
|
||||
|
||||
/* Increase the counter. */
|
||||
emit_label (end_of_code_label);
|
||||
|
||||
|
@ -5350,8 +5350,8 @@ tree_block_ends_with_call_p (basic_block bb)
|
||||
static bool
|
||||
tree_block_ends_with_condjump_p (basic_block bb)
|
||||
{
|
||||
tree stmt = tsi_stmt (bsi_last (bb).tsi);
|
||||
return (TREE_CODE (stmt) == COND_EXPR);
|
||||
tree stmt = last_stmt (bb);
|
||||
return (stmt && TREE_CODE (stmt) == COND_EXPR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,6 +38,9 @@ struct basic_block_def;
|
||||
typedef struct basic_block_def *basic_block;
|
||||
#endif
|
||||
|
||||
/* True if the code is in ssa form. */
|
||||
extern bool in_ssa_p;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Attributes for SSA_NAMEs.
|
||||
|
||||
@ -796,9 +799,10 @@ extern void linear_transform_loops (struct loops *);
|
||||
|
||||
/* In tree-ssa-loop-ivopts.c */
|
||||
extern bool expr_invariant_in_loop_p (struct loop *, tree);
|
||||
/* In gimplify.c */
|
||||
|
||||
/* In gimplify.c */
|
||||
tree force_gimple_operand (tree, tree *, bool, tree);
|
||||
tree force_gimple_operand_bsi (block_stmt_iterator *, tree, bool, tree);
|
||||
|
||||
#include "tree-flow-inline.h"
|
||||
|
||||
|
@ -54,6 +54,9 @@ Boston, MA 02111-1307, USA. */
|
||||
Graph. ACM Transactions on Programming Languages and Systems,
|
||||
13(4):451-490, October 1991. */
|
||||
|
||||
/* True if the code is in ssa form. */
|
||||
bool in_ssa_p;
|
||||
|
||||
/* Structure to map a variable VAR to the set of blocks that contain
|
||||
definitions for VAR. */
|
||||
struct def_blocks_d
|
||||
@ -1773,6 +1776,7 @@ rewrite_into_ssa (void)
|
||||
sbitmap_free (interesting_blocks);
|
||||
|
||||
timevar_pop (TV_TREE_SSA_OTHER);
|
||||
in_ssa_p = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2511,6 +2511,8 @@ rewrite_out_of_ssa (void)
|
||||
|
||||
/* Mark arrays indexed with non-constant indices with TREE_ADDRESSABLE. */
|
||||
discover_nonconstant_array_refs ();
|
||||
|
||||
in_ssa_p = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -44,7 +44,12 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
#include "tree-pass.h"
|
||||
#include "timevar.h"
|
||||
#include "value-prof.h"
|
||||
#include "ggc.h"
|
||||
|
||||
static GTY(()) tree gcov_type_node;
|
||||
static GTY(()) tree tree_interval_profiler_fn;
|
||||
static GTY(()) tree tree_pow2_profiler_fn;
|
||||
static GTY(()) tree tree_one_value_profiler_fn;
|
||||
|
||||
|
||||
/* Do initialization work for the edge profiler. */
|
||||
@ -52,6 +57,43 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
static void
|
||||
tree_init_edge_profiler (void)
|
||||
{
|
||||
tree interval_profiler_fn_type;
|
||||
tree pow2_profiler_fn_type;
|
||||
tree one_value_profiler_fn_type;
|
||||
tree gcov_type_ptr;
|
||||
|
||||
if (!gcov_type_node)
|
||||
{
|
||||
gcov_type_node = get_gcov_type ();
|
||||
gcov_type_ptr = build_pointer_type (gcov_type_node);
|
||||
|
||||
/* void (*) (gcov_type *, gcov_type, int, unsigned) */
|
||||
interval_profiler_fn_type
|
||||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node,
|
||||
integer_type_node,
|
||||
unsigned_type_node, NULL_TREE);
|
||||
tree_interval_profiler_fn
|
||||
= build_fn_decl ("__gcov_interval_profiler",
|
||||
interval_profiler_fn_type);
|
||||
|
||||
/* void (*) (gcov_type *, gcov_type) */
|
||||
pow2_profiler_fn_type
|
||||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node,
|
||||
NULL_TREE);
|
||||
tree_pow2_profiler_fn = build_fn_decl ("__gcov_pow2_profiler",
|
||||
pow2_profiler_fn_type);
|
||||
|
||||
/* void (*) (gcov_type *, gcov_type) */
|
||||
one_value_profiler_fn_type
|
||||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node,
|
||||
NULL_TREE);
|
||||
tree_one_value_profiler_fn
|
||||
= build_fn_decl ("__gcov_one_value_profiler",
|
||||
one_value_profiler_fn_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Output instructions as GIMPLE trees to increment the edge
|
||||
@ -61,7 +103,6 @@ tree_init_edge_profiler (void)
|
||||
static void
|
||||
tree_gen_edge_profiler (int edgeno, edge e)
|
||||
{
|
||||
tree gcov_type_node = get_gcov_type ();
|
||||
tree tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tree tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tree ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno);
|
||||
@ -75,6 +116,18 @@ tree_gen_edge_profiler (int edgeno, edge e)
|
||||
bsi_insert_on_edge (e, stmt3);
|
||||
}
|
||||
|
||||
/* Emits code to get VALUE to instrument at BSI, and returns the
|
||||
variable containing the value. */
|
||||
|
||||
static tree
|
||||
prepare_instrumented_value (block_stmt_iterator *bsi,
|
||||
histogram_value value)
|
||||
{
|
||||
tree val = value->hvalue.tree.value;
|
||||
return force_gimple_operand_bsi (bsi, fold_convert (gcov_type_node, val),
|
||||
true, NULL_TREE);
|
||||
}
|
||||
|
||||
/* Output instructions as GIMPLE trees to increment the interval histogram
|
||||
counter. VALUE is the expression whose value is profiled. TAG is the
|
||||
tag of the section for counters, BASE is offset of the counter position. */
|
||||
@ -82,163 +135,24 @@ tree_gen_edge_profiler (int edgeno, edge e)
|
||||
static void
|
||||
tree_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
{
|
||||
tree op, op1, op2, op1copy, op2copy;
|
||||
tree tmp1, tmp2, tmp3, val, index;
|
||||
tree label_decl2, label_decl3, label_decl4, label_decl5, label_decl6;
|
||||
edge e12, e23, e34, e45, e56;
|
||||
tree label2, label3, label4, label5, label6;
|
||||
tree stmt1, stmt2, stmt3, stmt4;
|
||||
/* Initializations are to prevent bogus uninitialized warnings. */
|
||||
tree bb1end = NULL_TREE, bb2end = NULL_TREE, bb3end = NULL_TREE;
|
||||
tree bb4end = NULL_TREE, bb5end = NULL_TREE;
|
||||
tree ref = tree_coverage_counter_ref (tag, base), ref2;
|
||||
basic_block bb2, bb3, bb4, bb5, bb6;
|
||||
tree stmt = value->hvalue.tree.stmt;
|
||||
block_stmt_iterator bsi = bsi_for_stmt (stmt);
|
||||
basic_block bb = bb_for_stmt (stmt);
|
||||
tree gcov_type_node = get_gcov_type ();
|
||||
tree optype;
|
||||
|
||||
op = stmt;
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR
|
||||
&& TREE_OPERAND (stmt, 0)
|
||||
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
|
||||
op = TREE_OPERAND (stmt, 0);
|
||||
/* op == MODIFY_EXPR */
|
||||
op = TREE_OPERAND (op, 1);
|
||||
/* op == TRUNC_DIV or TRUNC_MOD */
|
||||
op1 = TREE_OPERAND (op, 0);
|
||||
op2 = TREE_OPERAND (op, 1);
|
||||
optype = TREE_TYPE (op);
|
||||
|
||||
/* Blocks:
|
||||
Original = 1
|
||||
For 2nd compare = 2
|
||||
Normal case, neither more nor less = 3
|
||||
More = 4
|
||||
Less = 5
|
||||
End = 6 */
|
||||
label_decl2 = create_artificial_label ();
|
||||
label_decl3 = create_artificial_label ();
|
||||
label_decl4 = create_artificial_label ();
|
||||
label_decl5 = create_artificial_label ();
|
||||
label_decl6 = create_artificial_label ();
|
||||
|
||||
/* Do not evaluate op1 or op2 more than once. Probably
|
||||
volatile loads are the only things that could cause
|
||||
a problem, but this is harmless in any case. */
|
||||
op1copy = create_tmp_var (optype, "PROF");
|
||||
op2copy = create_tmp_var (optype, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, optype, op1copy, op1);
|
||||
stmt2 = build2 (MODIFY_EXPR, optype, op2copy, op2);
|
||||
TREE_OPERAND (op, 0) = op1copy;
|
||||
TREE_OPERAND (op, 1) = op2copy;
|
||||
|
||||
val = create_tmp_var (optype, "PROF");
|
||||
stmt3 = build2 (MODIFY_EXPR, optype, val,
|
||||
build2 (TRUNC_DIV_EXPR, optype, op1copy, op2copy));
|
||||
stmt4 = build2 (MODIFY_EXPR, optype, val,
|
||||
build2 (MINUS_EXPR, optype, val,
|
||||
build_int_cst (optype, value->hdata.intvl.int_start)));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
|
||||
index = create_tmp_var (gcov_type_node, "PROF");
|
||||
|
||||
/* Check for too big. */
|
||||
stmt1 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (GE_EXPR, boolean_type_node, val,
|
||||
build_int_cst (optype, value->hdata.intvl.steps)),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl4),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl2));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bb1end = stmt1;
|
||||
|
||||
/* Check for too small. */
|
||||
label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
|
||||
bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
|
||||
stmt1 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (LT_EXPR, boolean_type_node, val, integer_zero_node),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl5),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl3));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bb2end = stmt1;
|
||||
|
||||
/* Normal case, within range. */
|
||||
label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
|
||||
bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, index,
|
||||
build1 (NOP_EXPR, gcov_type_node, val));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bb3end = stmt1;
|
||||
|
||||
/* Too big */
|
||||
label4 = build1 (LABEL_EXPR, void_type_node, label_decl4);
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, index,
|
||||
build_int_cst (gcov_type_node, value->hdata.intvl.steps));
|
||||
bsi_insert_before (&bsi, label4, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bb4end = stmt1;
|
||||
|
||||
/* Too small */
|
||||
label5 = build1 (LABEL_EXPR, void_type_node, label_decl5);
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, index,
|
||||
build_int_cst (gcov_type_node,
|
||||
value->hdata.intvl.steps + 1));
|
||||
bsi_insert_before (&bsi, label5, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bb5end = stmt1;
|
||||
|
||||
/* Increment appropriate counter. */
|
||||
label6 = build1 (LABEL_EXPR, void_type_node, label_decl6);
|
||||
bsi_insert_before (&bsi, label6, BSI_SAME_STMT);
|
||||
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp3 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1,
|
||||
build2 (PLUS_EXPR, gcov_type_node, index,
|
||||
TREE_OPERAND (ref, 1)));
|
||||
TREE_OPERAND (ref, 1) = tmp1;
|
||||
/* Make a copy to avoid sharing complaints. */
|
||||
ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0),
|
||||
TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2),
|
||||
TREE_OPERAND (ref, 3));
|
||||
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2, ref);
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, tmp3,
|
||||
build2 (PLUS_EXPR, gcov_type_node, tmp2, integer_one_node));
|
||||
stmt4 = build2 (MODIFY_EXPR, gcov_type_node, ref2, tmp3);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
|
||||
/* Now fix up the CFG. */
|
||||
/* 1->2,4; 2->3,5; 3->6; 4->6; 5->6 */
|
||||
e12 = split_block (bb, bb1end);
|
||||
bb2 = e12->dest;
|
||||
e23 = split_block (bb2, bb2end);
|
||||
bb3 = e23->dest;
|
||||
e34 = split_block (bb3, bb3end);
|
||||
bb4 = e34->dest;
|
||||
e45 = split_block (bb4, bb4end);
|
||||
bb5 = e45->dest;
|
||||
e56 = split_block (bb5, bb5end);
|
||||
bb6 = e56->dest;
|
||||
|
||||
e12->flags &= ~EDGE_FALLTHRU;
|
||||
e12->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb, bb4, EDGE_TRUE_VALUE);
|
||||
e23->flags &= ~EDGE_FALLTHRU;
|
||||
e23->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb2, bb5, EDGE_TRUE_VALUE);
|
||||
remove_edge (e34);
|
||||
make_edge (bb3, bb6, EDGE_FALLTHRU);
|
||||
remove_edge (e45);
|
||||
make_edge (bb4, bb6, EDGE_FALLTHRU);
|
||||
tree ref = tree_coverage_counter_ref (tag, base), ref_ptr;
|
||||
tree args, call, val;
|
||||
tree start = build_int_cst_type (integer_type_node, value->hdata.intvl.int_start);
|
||||
tree steps = build_int_cst_type (unsigned_type_node, value->hdata.intvl.steps);
|
||||
|
||||
ref_ptr = force_gimple_operand_bsi (&bsi,
|
||||
build_addr (ref),
|
||||
true, NULL_TREE);
|
||||
val = prepare_instrumented_value (&bsi, value);
|
||||
args = tree_cons (NULL_TREE, ref_ptr,
|
||||
tree_cons (NULL_TREE, val,
|
||||
tree_cons (NULL_TREE, start,
|
||||
tree_cons (NULL_TREE, steps,
|
||||
NULL_TREE))));
|
||||
call = build_function_call_expr (tree_interval_profiler_fn, args);
|
||||
bsi_insert_before (&bsi, call, BSI_SAME_STMT);
|
||||
}
|
||||
|
||||
/* Output instructions as GIMPLE trees to increment the power of two histogram
|
||||
@ -248,162 +162,20 @@ tree_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
static void
|
||||
tree_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
{
|
||||
tree op;
|
||||
tree tmp1, tmp2, tmp3;
|
||||
tree index, denom;
|
||||
tree label_decl1 = create_artificial_label ();
|
||||
tree label_decl2 = create_artificial_label ();
|
||||
tree label_decl3 = create_artificial_label ();
|
||||
tree label1, label2, label3;
|
||||
tree stmt1, stmt2, stmt3, stmt4;
|
||||
tree bb1end, bb2end, bb3end;
|
||||
tree ref = tree_coverage_counter_ref (tag, base), ref2;
|
||||
basic_block bb2, bb3, bb4;
|
||||
tree stmt = value->hvalue.tree.stmt;
|
||||
block_stmt_iterator bsi = bsi_for_stmt (stmt);
|
||||
basic_block bb = bb_for_stmt (stmt);
|
||||
tree gcov_type_node = get_gcov_type ();
|
||||
tree optype, optypesigned, optypeunsigned;
|
||||
|
||||
op = stmt;
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR
|
||||
&& TREE_OPERAND (stmt, 0)
|
||||
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
|
||||
op = TREE_OPERAND (stmt, 0);
|
||||
/* op == MODIFY_EXPR */
|
||||
op = TREE_OPERAND (op, 1);
|
||||
/* op == TRUNC_DIV or TRUNC_MOD */
|
||||
op = TREE_OPERAND (op, 1);
|
||||
/* op == denominator */
|
||||
optype = TREE_TYPE (op);
|
||||
if (TYPE_UNSIGNED (optype))
|
||||
{
|
||||
/* Right shift must be unsigned. */
|
||||
optypeunsigned = optype;
|
||||
optypesigned = build_distinct_type_copy (optype);
|
||||
TYPE_UNSIGNED (optypesigned) = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Compare to zero must be signed. */
|
||||
optypesigned = optype;
|
||||
optypeunsigned = build_distinct_type_copy (optype);
|
||||
TYPE_UNSIGNED (optypeunsigned) = true;
|
||||
}
|
||||
|
||||
/* Set up variables and check if denominator is negative when considered
|
||||
as signed. */
|
||||
index = create_tmp_var (gcov_type_node, "PROF");
|
||||
denom = create_tmp_var (optype, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, index, integer_zero_node);
|
||||
stmt2 = build2 (MODIFY_EXPR, optype, denom, op);
|
||||
if (optypesigned == optype)
|
||||
{
|
||||
tmp1 = denom;
|
||||
stmt3 = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp1 = create_tmp_var (optypesigned, "PROF");
|
||||
stmt3 = build2 (MODIFY_EXPR, optypesigned, tmp1,
|
||||
build1 (NOP_EXPR, optypesigned, denom));
|
||||
}
|
||||
stmt4 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (LE_EXPR, boolean_type_node, tmp1, integer_zero_node),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl3),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl1));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
if (stmt3)
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
bb1end = stmt4;
|
||||
|
||||
/* Nonnegative. Check if denominator is power of 2. */
|
||||
label1 = build1 (LABEL_EXPR, void_type_node, label_decl1);
|
||||
tmp1 = create_tmp_var (optype, "PROF");
|
||||
tmp2 = create_tmp_var (optype, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, optype, tmp1,
|
||||
build2 (PLUS_EXPR, optype, denom, integer_minus_one_node));
|
||||
stmt2 = build2 (MODIFY_EXPR, optype, tmp2,
|
||||
build2 (BIT_AND_EXPR, optype, tmp1, denom));
|
||||
stmt3 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (NE_EXPR, boolean_type_node, tmp2, integer_zero_node),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl3),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl2));
|
||||
bsi_insert_before (&bsi, label1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bb2end = stmt3;
|
||||
|
||||
/* Loop. Increment index, shift denominator, repeat if denominator nonzero. */
|
||||
label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, index,
|
||||
build2 (PLUS_EXPR, gcov_type_node, index, integer_one_node));
|
||||
if (optypeunsigned == optype)
|
||||
{
|
||||
tmp1 = denom;
|
||||
stmt2 = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp1 = create_tmp_var (optypeunsigned, "PROF");
|
||||
stmt2 = build2 (MODIFY_EXPR, optypeunsigned, tmp1,
|
||||
build1 (NOP_EXPR, optypeunsigned, denom));
|
||||
}
|
||||
stmt3 = build2 (MODIFY_EXPR, optype, denom,
|
||||
build2 (RSHIFT_EXPR, optype, tmp1, integer_one_node));
|
||||
stmt4 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (NE_EXPR, boolean_type_node, denom, integer_zero_node),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl2),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl3));
|
||||
bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
if (stmt2)
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
bb3end = stmt4;
|
||||
|
||||
/* Increment the appropriate counter. */
|
||||
label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp3 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1,
|
||||
build2 (PLUS_EXPR, gcov_type_node,
|
||||
index, TREE_OPERAND (ref, 1)));
|
||||
TREE_OPERAND (ref, 1) = tmp1;
|
||||
/* Make a copy to avoid sharing complaints. */
|
||||
ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0),
|
||||
TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2), TREE_OPERAND (ref, 3));
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2, ref);
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, tmp3,
|
||||
build2 (PLUS_EXPR, gcov_type_node, tmp2, integer_one_node));
|
||||
stmt4 = build2 (MODIFY_EXPR, gcov_type_node, ref2, tmp3);
|
||||
bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
|
||||
/* Now fix up the CFG. */
|
||||
bb2 = (split_block (bb, bb1end))->dest;
|
||||
bb3 = (split_block (bb2, bb2end))->dest;
|
||||
bb4 = (split_block (bb3, bb3end))->dest;
|
||||
|
||||
EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU;
|
||||
EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb, bb4, EDGE_TRUE_VALUE);
|
||||
|
||||
EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU;
|
||||
EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb2, bb4, EDGE_TRUE_VALUE);
|
||||
|
||||
EDGE_SUCC (bb3, 0)->flags &= ~EDGE_FALLTHRU;
|
||||
EDGE_SUCC (bb3, 0)->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb3, bb3, EDGE_TRUE_VALUE);
|
||||
tree ref = tree_coverage_counter_ref (tag, base), ref_ptr;
|
||||
tree args, call, val;
|
||||
|
||||
ref_ptr = force_gimple_operand_bsi (&bsi,
|
||||
build_addr (ref),
|
||||
true, NULL_TREE);
|
||||
val = prepare_instrumented_value (&bsi, value);
|
||||
args = tree_cons (NULL_TREE, ref_ptr,
|
||||
tree_cons (NULL_TREE, val,
|
||||
NULL_TREE));
|
||||
call = build_function_call_expr (tree_pow2_profiler_fn, args);
|
||||
bsi_insert_before (&bsi, call, BSI_SAME_STMT);
|
||||
}
|
||||
|
||||
/* Output instructions as GIMPLE trees for code to find the most common value.
|
||||
@ -413,146 +185,20 @@ tree_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
static void
|
||||
tree_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base)
|
||||
{
|
||||
tree op;
|
||||
tree tmp1, tmp2, tmp3;
|
||||
tree label_decl1 = create_artificial_label ();
|
||||
tree label_decl2 = create_artificial_label ();
|
||||
tree label_decl3 = create_artificial_label ();
|
||||
tree label_decl4 = create_artificial_label ();
|
||||
tree label_decl5 = create_artificial_label ();
|
||||
tree label1, label2, label3, label4, label5;
|
||||
tree stmt1, stmt2, stmt3, stmt4;
|
||||
tree bb1end, bb2end, bb3end, bb4end, bb5end;
|
||||
tree ref1 = tree_coverage_counter_ref (tag, base);
|
||||
tree ref2 = tree_coverage_counter_ref (tag, base + 1);
|
||||
tree ref3 = tree_coverage_counter_ref (tag, base + 2);
|
||||
basic_block bb2, bb3, bb4, bb5, bb6;
|
||||
tree stmt = value->hvalue.tree.stmt;
|
||||
block_stmt_iterator bsi = bsi_for_stmt (stmt);
|
||||
basic_block bb = bb_for_stmt (stmt);
|
||||
tree gcov_type_node = get_gcov_type ();
|
||||
tree optype;
|
||||
|
||||
op = stmt;
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR
|
||||
&& TREE_OPERAND (stmt, 0)
|
||||
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
|
||||
op = TREE_OPERAND (stmt, 0);
|
||||
/* op == MODIFY_EXPR */
|
||||
op = TREE_OPERAND (op, 1);
|
||||
/* op == TRUNC_DIV or TRUNC_MOD */
|
||||
op = TREE_OPERAND (op, 1);
|
||||
/* op == denominator */
|
||||
optype = TREE_TYPE (op);
|
||||
|
||||
/* Check if the stored value matches. */
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (optype, "PROF");
|
||||
tmp3 = create_tmp_var (optype, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1, ref1);
|
||||
stmt2 = build2 (MODIFY_EXPR, optype, tmp2,
|
||||
build1 (NOP_EXPR, optype, tmp1));
|
||||
stmt3 = build2 (MODIFY_EXPR, optype, tmp3, op);
|
||||
stmt4 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (EQ_EXPR, boolean_type_node, tmp2, tmp3),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl4),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl1));
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
|
||||
bb1end = stmt4;
|
||||
|
||||
/* Does not match; check whether the counter is zero. */
|
||||
label1 = build1 (LABEL_EXPR, void_type_node, label_decl1);
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1, ref2);
|
||||
stmt2 = build3 (COND_EXPR, void_type_node,
|
||||
build2 (EQ_EXPR, boolean_type_node, tmp1, integer_zero_node),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl3),
|
||||
build1 (GOTO_EXPR, void_type_node, label_decl2));
|
||||
bsi_insert_before (&bsi, label1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bb2end = stmt2;
|
||||
|
||||
/* Counter is not zero yet, decrement. */
|
||||
label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1, ref2);
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2,
|
||||
build (MINUS_EXPR, gcov_type_node, tmp1, integer_one_node));
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, ref2, tmp2);
|
||||
bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bb3end = stmt3;
|
||||
|
||||
/* Counter was zero, store new value. */
|
||||
label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
|
||||
tmp1 = create_tmp_var (optype, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, optype, tmp1, op);
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2,
|
||||
build1 (NOP_EXPR, gcov_type_node, tmp1));
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, ref1, tmp2);
|
||||
bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bb4end = stmt3;
|
||||
/* (fall through) */
|
||||
|
||||
/* Increment counter. */
|
||||
label4 = build1 (LABEL_EXPR, void_type_node, label_decl4);
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1, ref2);
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2,
|
||||
build (PLUS_EXPR, gcov_type_node, tmp1, integer_one_node));
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, ref2, tmp2);
|
||||
bsi_insert_before (&bsi, label4, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
bb5end = stmt3;
|
||||
|
||||
/* Increment the counter of all executions; this seems redundant given
|
||||
that we have counts for edges in cfg, but it may happen that some
|
||||
optimization will change the counts for the block (either because
|
||||
it is unable to update them correctly, or because it will duplicate
|
||||
the block or its part). */
|
||||
label5 = build1 (LABEL_EXPR, void_type_node, label_decl5);
|
||||
tmp1 = create_tmp_var (gcov_type_node, "PROF");
|
||||
tmp2 = create_tmp_var (gcov_type_node, "PROF");
|
||||
stmt1 = build2 (MODIFY_EXPR, gcov_type_node, tmp1, ref3);
|
||||
stmt2 = build2 (MODIFY_EXPR, gcov_type_node, tmp2,
|
||||
build (PLUS_EXPR, gcov_type_node, tmp1, integer_one_node));
|
||||
stmt3 = build2 (MODIFY_EXPR, gcov_type_node, ref3, tmp2);
|
||||
bsi_insert_before (&bsi, label5, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
|
||||
bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
|
||||
|
||||
/* Now fix up the CFG. */
|
||||
bb2 = (split_block (bb, bb1end))->dest;
|
||||
bb3 = (split_block (bb2, bb2end))->dest;
|
||||
bb4 = (split_block (bb3, bb3end))->dest;
|
||||
bb5 = (split_block (bb4, bb4end))->dest;
|
||||
bb6 = (split_block (bb5, bb5end))->dest;
|
||||
|
||||
EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU;
|
||||
EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb, bb5, EDGE_TRUE_VALUE);
|
||||
|
||||
EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU;
|
||||
EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE;
|
||||
make_edge (bb2, bb4, EDGE_TRUE_VALUE);
|
||||
|
||||
remove_edge (EDGE_SUCC (bb3, 0));
|
||||
make_edge (bb3, bb6, EDGE_FALLTHRU);
|
||||
tree ref = tree_coverage_counter_ref (tag, base), ref_ptr;
|
||||
tree args, call, val;
|
||||
|
||||
ref_ptr = force_gimple_operand_bsi (&bsi,
|
||||
build_addr (ref),
|
||||
true, NULL_TREE);
|
||||
val = prepare_instrumented_value (&bsi, value);
|
||||
args = tree_cons (NULL_TREE, ref_ptr,
|
||||
tree_cons (NULL_TREE, val,
|
||||
NULL_TREE));
|
||||
call = build_function_call_expr (tree_one_value_profiler_fn, args);
|
||||
bsi_insert_before (&bsi, call, BSI_SAME_STMT);
|
||||
}
|
||||
|
||||
/* Output instructions as GIMPLE trees for code to find the most common value
|
||||
@ -637,3 +283,5 @@ struct profile_hooks tree_profile_hooks =
|
||||
tree_gen_const_delta_profiler,/* gen_const_delta_profiler */
|
||||
tree_profile_dump_file /* profile_dump_file */
|
||||
};
|
||||
|
||||
#include "gt-tree-profile.h"
|
||||
|
17
gcc/tree.c
17
gcc/tree.c
@ -2807,6 +2807,23 @@ build_decl_stat (enum tree_code code, tree name, tree type MEM_STAT_DECL)
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Builds and returns function declaration with NAME and TYPE. */
|
||||
|
||||
tree
|
||||
build_fn_decl (const char *name, tree type)
|
||||
{
|
||||
tree id = get_identifier (name);
|
||||
tree decl = build_decl (FUNCTION_DECL, id, type);
|
||||
|
||||
DECL_EXTERNAL (decl) = 1;
|
||||
TREE_PUBLIC (decl) = 1;
|
||||
DECL_ARTIFICIAL (decl) = 1;
|
||||
TREE_NOTHROW (decl) = 1;
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
|
||||
/* BLOCK nodes are used to represent the structure of binding contours
|
||||
and declarations, once those contours have been exited and their contents
|
||||
|
@ -2905,6 +2905,7 @@ extern tree build_string (int, const char *);
|
||||
extern tree build_tree_list_stat (tree, tree MEM_STAT_DECL);
|
||||
#define build_tree_list(t,q) build_tree_list_stat(t,q MEM_STAT_INFO)
|
||||
extern tree build_decl_stat (enum tree_code, tree, tree MEM_STAT_DECL);
|
||||
extern tree build_fn_decl (const char *, tree);
|
||||
#define build_decl(c,t,q) build_decl_stat (c,t,q MEM_STAT_INFO)
|
||||
extern tree build_block (tree, tree, tree, tree, tree);
|
||||
#ifndef USE_MAPPED_LOCATION
|
||||
|
113
gcc/value-prof.c
113
gcc/value-prof.c
@ -128,8 +128,6 @@ static bool rtl_mod_subtract_transform (rtx);
|
||||
#ifdef HAVE_prefetch
|
||||
static bool speculative_prefetching_transform (rtx);
|
||||
#endif
|
||||
static void tree_divmod_values_to_profile (tree, histogram_values *);
|
||||
static void tree_values_to_profile (tree, histogram_values *);
|
||||
static tree tree_divmod_fixed_value (tree, tree, tree, tree,
|
||||
tree, int, gcov_type, gcov_type);
|
||||
static tree tree_mod_pow2 (tree, tree, tree, tree, int, gcov_type, gcov_type);
|
||||
@ -181,7 +179,6 @@ rtl_divmod_values_to_profile (rtx insn, histogram_values *values)
|
||||
hist->hvalue.rtl.mode = mode;
|
||||
hist->hvalue.rtl.insn = insn;
|
||||
hist->type = HIST_TYPE_POW2;
|
||||
hist->hdata.pow2.may_be_other = 1;
|
||||
VEC_safe_push (histogram_value, heap, *values, hist);
|
||||
}
|
||||
|
||||
@ -360,9 +357,7 @@ rtl_find_values_to_profile (histogram_values *values)
|
||||
fprintf (dump_file,
|
||||
"Pow2 counter for insn %d.\n",
|
||||
INSN_UID ((rtx)hist->hvalue.rtl.insn));
|
||||
hist->n_counters
|
||||
= GET_MODE_BITSIZE (hist->hvalue.rtl.mode)
|
||||
+ (hist->hdata.pow2.may_be_other ? 1 : 0);
|
||||
hist->n_counters = 2;
|
||||
break;
|
||||
|
||||
case HIST_TYPE_SINGLE_VALUE:
|
||||
@ -722,7 +717,7 @@ rtl_mod_pow2_value_transform (rtx insn)
|
||||
enum machine_mode mode;
|
||||
gcov_type wrong_values, count;
|
||||
edge e;
|
||||
int i, all, prob;
|
||||
int all, prob;
|
||||
|
||||
set = single_set (insn);
|
||||
if (!set)
|
||||
@ -751,15 +746,9 @@ rtl_mod_pow2_value_transform (rtx insn)
|
||||
histogram = XEXP (XEXP (histogram, 0), 1);
|
||||
value = XEXP (histogram, 0);
|
||||
histogram = XEXP (histogram, 1);
|
||||
wrong_values =INTVAL (XEXP (histogram, 0));
|
||||
wrong_values = INTVAL (XEXP (histogram, 0));
|
||||
histogram = XEXP (histogram, 1);
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < GET_MODE_BITSIZE (mode); i++)
|
||||
{
|
||||
count += INTVAL (XEXP (histogram, 0));
|
||||
histogram = XEXP (histogram, 1);
|
||||
}
|
||||
count = INTVAL (XEXP (histogram, 0));
|
||||
|
||||
if (!rtx_equal_p (op2, value))
|
||||
return false;
|
||||
@ -1355,7 +1344,6 @@ tree_mod_pow2_value_transform (tree stmt)
|
||||
gcov_type count, wrong_values, all;
|
||||
tree modify, op, op1, op2, result, value;
|
||||
int prob;
|
||||
unsigned int i;
|
||||
|
||||
modify = stmt;
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR
|
||||
@ -1386,9 +1374,7 @@ tree_mod_pow2_value_transform (tree stmt)
|
||||
|
||||
value = histogram->hvalue.tree.value;
|
||||
wrong_values = histogram->hvalue.tree.counters[0];
|
||||
count = 0;
|
||||
for (i = 1; i <= TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (stmt))); i++)
|
||||
count += histogram->hvalue.tree.counters[i];
|
||||
count = histogram->hvalue.tree.counters[1];
|
||||
|
||||
/* We require that we hit a power of 2 at least half of all evaluations. */
|
||||
if (simple_cst_equal (op2, value) != 1 || count < wrong_values)
|
||||
@ -1576,10 +1562,6 @@ tree_mod_subtract_transform (tree stmt)
|
||||
wrong_values += histogram->hvalue.tree.counters[i+1];
|
||||
all += wrong_values;
|
||||
|
||||
/* Sanity check. */
|
||||
if (simple_cst_equal (op2, value) != 1)
|
||||
return false;
|
||||
|
||||
/* We require that we use just subtractions in at least 50% of all
|
||||
evaluations. */
|
||||
count = 0;
|
||||
@ -1638,54 +1620,53 @@ rtl_register_value_prof_hooks (void)
|
||||
gcc_assert (!ir_type ());
|
||||
}
|
||||
|
||||
/* Find values inside INSN for that we want to measure histograms for
|
||||
/* Find values inside STMT for that we want to measure histograms for
|
||||
division/modulo optimization. */
|
||||
static void
|
||||
tree_divmod_values_to_profile (tree stmt, histogram_values *values)
|
||||
{
|
||||
tree op, op1, op2;
|
||||
tree assign, lhs, rhs, divisor, op0, type;
|
||||
histogram_value hist;
|
||||
|
||||
op = stmt;
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR
|
||||
&& TREE_OPERAND (stmt, 0)
|
||||
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
|
||||
op = TREE_OPERAND (stmt, 0);
|
||||
if (TREE_CODE (stmt) == RETURN_EXPR)
|
||||
assign = TREE_OPERAND (stmt, 0);
|
||||
else
|
||||
assign = stmt;
|
||||
|
||||
if (TREE_CODE (op) != MODIFY_EXPR)
|
||||
if (!assign
|
||||
|| TREE_CODE (assign) != MODIFY_EXPR)
|
||||
return;
|
||||
if (!INTEGRAL_TYPE_P (TREE_TYPE (op)))
|
||||
lhs = TREE_OPERAND (assign, 0);
|
||||
type = TREE_TYPE (lhs);
|
||||
if (!INTEGRAL_TYPE_P (type))
|
||||
return;
|
||||
op = TREE_OPERAND (op, 1);
|
||||
switch (TREE_CODE (op))
|
||||
|
||||
rhs = TREE_OPERAND (assign, 1);
|
||||
switch (TREE_CODE (rhs))
|
||||
{
|
||||
case TRUNC_DIV_EXPR:
|
||||
case TRUNC_MOD_EXPR:
|
||||
op1 = TREE_OPERAND (op, 0);
|
||||
op2 = TREE_OPERAND (op, 1);
|
||||
divisor = TREE_OPERAND (rhs, 1);
|
||||
op0 = TREE_OPERAND (rhs, 0);
|
||||
|
||||
VEC_reserve (histogram_value, heap, *values, 3);
|
||||
|
||||
/* Check for a special case where the divisor is power(s) of 2.
|
||||
This is more aggressive than the RTL version, under the
|
||||
assumption that later phases will reduce / or % by power of 2
|
||||
to something clever most of the time. Signed or unsigned. */
|
||||
if (TREE_CODE (op2) != INTEGER_CST)
|
||||
|
||||
if (is_gimple_reg (divisor))
|
||||
{
|
||||
/* Check for a special case where the divisor is power(s) of 2.
|
||||
This is more aggressive than the RTL version, under the
|
||||
assumption that later phases will reduce / or % by power of 2
|
||||
to something clever most of the time. Signed or unsigned. */
|
||||
hist = ggc_alloc (sizeof (*hist));
|
||||
hist->hvalue.tree.value = op2;
|
||||
hist->hvalue.tree.value = divisor;
|
||||
hist->hvalue.tree.stmt = stmt;
|
||||
hist->type = HIST_TYPE_POW2;
|
||||
hist->hdata.pow2.may_be_other = 1;
|
||||
VEC_quick_push (histogram_value, *values, hist);
|
||||
}
|
||||
|
||||
/* Check for the case where the divisor is the same value most
|
||||
of the time. */
|
||||
if (TREE_CODE (op2) != INTEGER_CST)
|
||||
{
|
||||
/* Check for the case where the divisor is the same value most
|
||||
of the time. */
|
||||
hist = ggc_alloc (sizeof (*hist));
|
||||
hist->hvalue.tree.value = op2;
|
||||
hist->hvalue.tree.value = divisor;
|
||||
hist->hvalue.tree.stmt = stmt;
|
||||
hist->type = HIST_TYPE_SINGLE_VALUE;
|
||||
VEC_quick_push (histogram_value, *values, hist);
|
||||
@ -1693,11 +1674,13 @@ tree_divmod_values_to_profile (tree stmt, histogram_values *values)
|
||||
|
||||
/* For mod, check whether it is not often a noop (or replaceable by
|
||||
a few subtractions). */
|
||||
if (TREE_CODE (op) == TRUNC_MOD_EXPR && TYPE_UNSIGNED (TREE_TYPE (op)))
|
||||
if (TREE_CODE (rhs) == TRUNC_MOD_EXPR
|
||||
&& TYPE_UNSIGNED (type))
|
||||
{
|
||||
hist = ggc_alloc (sizeof (*hist));
|
||||
hist->hvalue.tree.stmt = stmt;
|
||||
hist->hvalue.tree.value = op2;
|
||||
hist->hvalue.tree.value
|
||||
= build2 (TRUNC_DIV_EXPR, type, op0, divisor);
|
||||
hist->type = HIST_TYPE_INTERVAL;
|
||||
hist->hdata.intvl.int_start = 0;
|
||||
hist->hdata.intvl.steps = 2;
|
||||
@ -1710,8 +1693,9 @@ tree_divmod_values_to_profile (tree stmt, histogram_values *values)
|
||||
}
|
||||
}
|
||||
|
||||
/* Find values inside INSN for that we want to measure histograms and adds
|
||||
them to list VALUES (increasing the record of its length in N_VALUES). */
|
||||
/* Find values inside STMT for that we want to measure histograms and adds
|
||||
them to list VALUES. */
|
||||
|
||||
static void
|
||||
tree_values_to_profile (tree stmt, histogram_values *values)
|
||||
{
|
||||
@ -1724,17 +1708,13 @@ tree_find_values_to_profile (histogram_values *values)
|
||||
{
|
||||
basic_block bb;
|
||||
block_stmt_iterator bsi;
|
||||
tree stmt;
|
||||
unsigned int i;
|
||||
unsigned i;
|
||||
histogram_value hist;
|
||||
|
||||
|
||||
*values = NULL;
|
||||
FOR_EACH_BB (bb)
|
||||
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
|
||||
{
|
||||
tree stmt = bsi_stmt (bsi);
|
||||
tree_values_to_profile (stmt, values);
|
||||
}
|
||||
tree_values_to_profile (bsi_stmt (bsi), values);
|
||||
static_values = *values;
|
||||
|
||||
for (i = 0; VEC_iterate (histogram_value, *values, i, hist); i++)
|
||||
@ -1758,20 +1738,17 @@ tree_find_values_to_profile (histogram_values *values)
|
||||
case HIST_TYPE_POW2:
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Pow2 counter for insn ");
|
||||
fprintf (dump_file, "Pow2 counter for tree ");
|
||||
print_generic_expr (dump_file, hist->hvalue.tree.stmt, TDF_SLIM);
|
||||
fprintf (dump_file, ".\n");
|
||||
}
|
||||
stmt = hist->hvalue.tree.stmt;
|
||||
hist->n_counters
|
||||
= TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (stmt)))
|
||||
+ (hist->hdata.pow2.may_be_other ? 1 : 0);
|
||||
hist->n_counters = 2;
|
||||
break;
|
||||
|
||||
case HIST_TYPE_SINGLE_VALUE:
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Single value counter for insn ");
|
||||
fprintf (dump_file, "Single value counter for tree ");
|
||||
print_generic_expr (dump_file, hist->hvalue.tree.stmt, TDF_SLIM);
|
||||
fprintf (dump_file, ".\n");
|
||||
}
|
||||
@ -1781,7 +1758,7 @@ tree_find_values_to_profile (histogram_values *values)
|
||||
case HIST_TYPE_CONST_DELTA:
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Constant delta counter for insn ");
|
||||
fprintf (dump_file, "Constant delta counter for tree ");
|
||||
print_generic_expr (dump_file, hist->hvalue.tree.stmt, TDF_SLIM);
|
||||
fprintf (dump_file, ".\n");
|
||||
}
|
||||
|
@ -66,10 +66,6 @@ struct histogram_value_t
|
||||
int int_start; /* First value in interval. */
|
||||
unsigned int steps; /* Number of values in it. */
|
||||
} intvl; /* Interval histogram data. */
|
||||
struct
|
||||
{
|
||||
int may_be_other; /* If the value may be non-positive or not 2^k. */
|
||||
} pow2; /* Power of 2 histogram data. */
|
||||
} hdata; /* Profiled information specific data. */
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user