#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "flags.h"
#include "toplev.h"
#include "output.h"
#include "expr.h"
#include "feedback.h"
#include "basic-block.h"
#include "profile.h"
#include "tree-inline.h"
#include "gcov-io.h"
#include "ggc.h"
#include "langhooks.h"
#include "hashtab.h"
static tree feedback_counts_table;
static tree feedback_call_counts_table;
struct map GTY (()) { tree t; HOST_WIDE_INT i; };
static GTY ((param_is (struct map))) htab_t control_flow_table;
static GTY ((param_is (struct map))) htab_t call_table;
static GTY (()) varray_type control_flow_to_counter_table;
static GTY (()) varray_type call_to_counter_table;
static unsigned int call_counter = 0;
static unsigned int control_flow_counter = 0;
static GTY (()) varray_type control_flow_counts;
static GTY (()) varray_type call_counts;
static rtx create_feedback_counter_rtx PARAMS ((tree, int *, int *));
static tree find_decoratable_tree PARAMS ((tree *, int *, void *));
static int find_tree_in_table PARAMS ((htab_t, tree));
static hashval_t map_hash PARAMS ((const void *));
static int map_eq PARAMS ((const void*, const void*));
static FILE *db_file = 0;
static hashval_t map_hash (x)
const void *x;
{
struct map* p = (struct map*) x;
return htab_hash_pointer (p->t);
}
static int map_eq (x, y)
const void *x, *y;
{
struct map* p = (struct map*) x;
struct map* q = (struct map*) y;
return htab_eq_pointer (p->t, q->t);
}
static htab_t seen_fndecl;
void
init_feedback (filename)
const char* filename;
{
if (flag_create_feedback)
{
char name[20];
tree gcov_type_type = make_unsigned_type (GCOV_TYPE_SIZE);
tree domain_tree
= build_index_type (build_int_2 (1000, 0));
tree gcov_type_array_type
= build_array_type (gcov_type_type, domain_tree);
feedback_counts_table
= build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
TREE_STATIC (feedback_counts_table) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LFDO", 0);
DECL_NAME (feedback_counts_table) = get_identifier (name);
DECL_ALIGN (feedback_counts_table) = TYPE_ALIGN (gcov_type_type);
feedback_call_counts_table
= build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
TREE_STATIC (feedback_call_counts_table) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LFDO", 1);
DECL_NAME (feedback_call_counts_table) = get_identifier (name);
DECL_ALIGN (feedback_call_counts_table) = TYPE_ALIGN (gcov_type_type);
control_flow_table = htab_create_ggc (50, map_hash, map_eq, 0);
call_table = htab_create_ggc (50, map_hash, map_eq, 0);
VARRAY_INT_INIT (control_flow_to_counter_table, 50,
"cflowctr table");
VARRAY_INT_INIT (call_to_counter_table, 50, "callctr table");
}
if (flag_use_feedback)
{
int len = strlen(filename);
char *db_file_name = alloca (len + 4);
strcpy (db_file_name, filename);
strcat (db_file_name, ".db");
db_file = fopen (db_file_name, "rb");
if (!db_file)
{
warning ("file %s not found, execution counts assumed to be zero",
db_file_name);
flag_use_feedback = 0;
return;
}
control_flow_table = htab_create_ggc (50, map_hash, map_eq, 0);
call_table = htab_create_ggc (50, map_hash, map_eq, 0);
VARRAY_INT_INIT (control_flow_to_counter_table, 50,
"cflowtoctr table");
VARRAY_INT_INIT (call_to_counter_table, 50, "calltoctr table");
VARRAY_WIDEST_INT_INIT (control_flow_counts, 50, "cflowctr table");
VARRAY_WIDEST_INT_INIT (call_counts, 50, "callctr table");
if (db_file)
{
long ltemp;
gcov_type gtemp;
long nfuncs;
long size_of_header;
long narcs, ncalls, narcindices, ncallindices;
long j;
if (
(__read_long (<emp, db_file, 4) || ltemp != -457)
|| (__read_long (&nfuncs, db_file, 4))
|| (__read_long (&narcs, db_file, 4))
|| (__read_long (&ncalls, db_file, 4))
|| (__read_long (&narcindices, db_file, 4))
|| (__read_long (&ncallindices, db_file, 4))
|| (__read_long (&size_of_header, db_file, 4))
|| fseek (db_file, 7 * 4 + size_of_header, SEEK_SET))
{
read_error:;
warning ("file %s corrupted, ignored", db_file_name);
db_file = 0;
return;
}
for (j = narcs; j > 0; j--)
if (__read_gcov_type (>emp, db_file, 8))
goto read_error;
else
VARRAY_PUSH_WIDEST_INT (control_flow_counts, gtemp);
for (j = ncalls; j > 0; j--)
if (__read_gcov_type (>emp, db_file, 8))
goto read_error;
else
VARRAY_PUSH_WIDEST_INT (call_counts, gtemp);
for (j = narcindices; j > 0; j--)
if (__read_long (<emp, db_file, 4))
goto read_error;
else
VARRAY_PUSH_INT (control_flow_to_counter_table, ltemp);
for (j = ncallindices; j > 0; j--)
if (__read_long (<emp, db_file, 4))
goto read_error;
else
VARRAY_PUSH_INT (call_to_counter_table, ltemp);
}
}
seen_fndecl = htab_create (131, htab_hash_pointer,
htab_eq_pointer, NULL);
}
tree
end_feedback ()
{
if (flag_create_feedback && profile_info.count_instrumented_edges > 0)
{
{
tree gcov_type_type = make_unsigned_type (GCOV_TYPE_SIZE);
tree domain_tree
= build_index_type (build_int_2 (
MAX (0, profile_info.count_instrumented_edges-1), 0));
tree gcov_type_array_type
= build_array_type (gcov_type_type, domain_tree);
TREE_TYPE (feedback_counts_table) = gcov_type_array_type;
DECL_SIZE (feedback_counts_table) = TYPE_SIZE (gcov_type_array_type);
DECL_SIZE_UNIT (feedback_counts_table)
= TYPE_SIZE_UNIT (gcov_type_array_type);
assemble_variable (feedback_counts_table, 0, 0, 0);
}
if (profile_info.count_instrumented_calls > 0)
{
tree gcov_type_type = make_unsigned_type (GCOV_TYPE_SIZE);
tree domain_tree
= build_index_type (build_int_2 (
profile_info.count_instrumented_calls-1, 0));
tree gcov_type_array_type
= build_array_type (gcov_type_type, domain_tree);
TREE_TYPE (feedback_call_counts_table) = gcov_type_array_type;
DECL_SIZE (feedback_call_counts_table) = TYPE_SIZE (gcov_type_array_type);
DECL_SIZE_UNIT (feedback_call_counts_table)
= TYPE_SIZE_UNIT (gcov_type_array_type);
assemble_variable (feedback_call_counts_table, 0, 0, 0);
}
}
if (flag_use_feedback)
{
if (db_file)
fclose (db_file);
}
return feedback_counts_table;
}
void set_fdo_note_count (note, count)
rtx note;
HOST_WIDEST_INT count;
{
#if HOST_BITS_PER_WIDEST_INT == HOST_BITS_PER_WIDE_INT
NOTE_FDO_COUNT (note) = count;
#else
#if HOST_BITS_PER_WIDEST_INT == 2 * HOST_BITS_PER_WIDE_INT
union { HOST_WIDE_INT x[2]; HOST_WIDEST_INT y; } u;
u.y = count;
NOTE_FDO_COUNT_HIGH (note) = u.x[0];
NOTE_FDO_COUNT (note) = u.x[1];
#else
abort ();
#endif
#endif
}
HOST_WIDEST_INT get_fdo_note_count (note)
rtx note;
{
HOST_WIDEST_INT count;
#if HOST_BITS_PER_WIDEST_INT == HOST_BITS_PER_WIDE_INT
count = NOTE_FDO_COUNT (note);
#else
#if HOST_BITS_PER_WIDEST_INT == 2 * HOST_BITS_PER_WIDE_INT
union { HOST_WIDE_INT x[2]; HOST_WIDEST_INT y; } u;
u.x[0] = NOTE_FDO_COUNT_HIGH (note);
u.x[1] = NOTE_FDO_COUNT (note);
count = u.y;
#else
abort ();
#endif
#endif
return count;
}
static rtx
create_feedback_counter_rtx (table, total_counter, func_counter)
tree table;
int *total_counter;
int *func_counter;
{
tree t;
rtx insns_head = NULL_RTX;
t = (*lang_hooks.create_feedback_counter_tree) (table, *total_counter);
if (t)
{
(*total_counter)++;
(*func_counter)++;
start_sequence ();
expand_expr (t, NULL_RTX, VOIDmode, 0);
insns_head = get_insns ();
end_sequence ();
}
return insns_head;
}
static int
find_tree_in_table (table, t)
htab_t table;
tree t;
{
struct map temp;
struct map* entry;
temp.t = t;
entry = (struct map *)htab_find (table, (PTR)&temp);
if (entry)
return entry->i;
else
return -1;
}
void
emit_rtx_feedback_counter (t, offset, use)
tree t;
int offset;
enum fdo_note_kind use;
{
if (flag_create_feedback && offset >= 0)
{
int ix;
rtx r, note;
if ((unsigned)offset >= n_slots (t))
abort ();
r = create_feedback_counter_rtx (feedback_counts_table,
&profile_info.count_instrumented_edges,
&profile_info.count_edges_instrumented_now);
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_RTX);
NOTE_FDO_COUNT_RTX (note) = r;
ix = find_tree_in_table (control_flow_table, t);
if (ix == -1)
abort ();
VARRAY_INT (control_flow_to_counter_table, ix + offset) =
profile_info.count_instrumented_edges - 1;
}
else if (flag_use_feedback && use != fdo_none)
{
HOST_WIDEST_INT count;
rtx note;
if ((unsigned)offset >= n_slots (t))
abort ();
count = times_arc_executed (t, offset);
if (use == fdo_incoming)
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_INCOMING);
else if (use == fdo_outgoing)
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_OUTGOING);
else if (use == fdo_block)
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_BLOCK);
else if (use == fdo_tablejump)
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_TABLEJUMP);
else
abort ();
set_fdo_note_count (note, count);
}
}
unsigned int
n_slots (t)
tree t;
{
unsigned int size = 0;
switch (TREE_CODE (t))
{
case FUNCTION_DECL:
size = 3;
break;
case TRUTH_ORIF_EXPR:
case TRUTH_ANDIF_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
size = 2;
break;
case COND_EXPR:
size = 4;
break;
case ARRAY_REF:
case LABEL_DECL:
case MIN_EXPR:
case MAX_EXPR:
size = 1;
break;
case LT_EXPR:
case LE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case GE_EXPR:
case GT_EXPR:
case UNLT_EXPR:
case UNEQ_EXPR:
case UNGT_EXPR:
case UNGE_EXPR:
case UNLE_EXPR:
case UNORDERED_EXPR:
case ORDERED_EXPR:
size = 1;
break;
default:
size = (*lang_hooks.find_control_flow_tree) (t);
}
return size;
}
void
clone_rtx_feedback_counter (oldt, newt)
tree oldt;
tree newt;
{
if (!(seen_fndecl && htab_find_slot (seen_fndecl, current_function_decl, NO_INSERT)))
return;
if (flag_create_feedback || flag_use_feedback)
{
unsigned int n = n_slots (oldt);
int ix;
struct map *m;
struct map temp;
PTR *p;
if (n == 0)
return;
ix = find_tree_in_table (control_flow_table, oldt);
if (ix == -1)
abort ();
temp.t = newt;
m = (struct map *)htab_find (control_flow_table, (PTR)&temp);
if (m)
{
m->i = ix;
return;
}
m = (struct map *)ggc_alloc (sizeof (struct map));
m->t = newt;
p = htab_find_slot (control_flow_table, (PTR) m, INSERT);
m->i = ix;
*p = (PTR) m;
if (TREE_CODE (newt) == FUNCTION_DECL && DECL_SAVED_TREE (newt))
{
PTR slot = htab_find_slot (seen_fndecl, newt, INSERT);
*(tree *)slot = newt;
}
}
}
void
emit_rtx_call_feedback_counter (t)
tree t;
{
if (flag_create_feedback)
{
int ix;
rtx r, note;
ix = find_tree_in_table (call_table, t);
if (ix == -1)
return;
r = create_feedback_counter_rtx (feedback_call_counts_table,
&profile_info.count_instrumented_calls,
&profile_info.count_calls_instrumented_now);
note = emit_note (NULL, NOTE_INSN_FDO_COUNT_RTX);
NOTE_FDO_COUNT_RTX (note) = r;
VARRAY_INT (call_to_counter_table, ix) =
profile_info.count_instrumented_calls - 1;
}
}
static tree
find_decoratable_tree (t, i, j)
tree *t;
int *i ATTRIBUTE_UNUSED;
void *j ATTRIBUTE_UNUSED;
{
unsigned int size = 0;
if (TREE_CODE (*t) == CALL_EXPR)
{
struct map *m = (struct map *)ggc_alloc (sizeof (struct map));
PTR* p;
m->t = *t;
p = htab_find_slot (call_table, (PTR) m, INSERT);
if (flag_create_feedback)
{
VARRAY_PUSH_INT (call_to_counter_table, -1);
m->i = VARRAY_ACTIVE_SIZE (call_to_counter_table) - 1;
}
else
m->i = call_counter++;
*p = (PTR) m;
}
else
size = n_slots (*t);
if (size > 0)
{
struct map *m = (struct map *)ggc_alloc (sizeof (struct map));
PTR* p;
m->t = *t;
p = htab_find_slot (control_flow_table, (PTR) m, INSERT);
if (*p == NULL)
{
if (flag_create_feedback)
{
VARRAY_PUSH_INT (control_flow_to_counter_table, -1);
m->i = VARRAY_ACTIVE_SIZE (control_flow_to_counter_table) - 1;
while (--size > 0)
VARRAY_PUSH_INT (control_flow_to_counter_table, -1);
}
else
{
m->i = control_flow_counter;
control_flow_counter += size;
}
*p = (PTR) m;
}
}
return NULL_TREE;
}
void
decorate_for_feedback (fndecl)
tree fndecl;
{
if (DECL_SAVED_TREE (fndecl))
{
if (flag_create_feedback || flag_use_feedback)
{
if (getenv ("SECRET_DUMP"))
{
static int nfunc=0;
if (flag_create_feedback)
printf("function %d calls %d control flow %d\n",
nfunc, VARRAY_ACTIVE_SIZE(call_to_counter_table),
VARRAY_ACTIVE_SIZE(control_flow_to_counter_table));
else
printf("function %d calls %d control flow %d\n",
nfunc, call_counter, control_flow_counter);
nfunc++;
}
struct map *m = (struct map *)ggc_alloc (sizeof (struct map));
PTR* p;
PTR slot = htab_find_slot (seen_fndecl, fndecl, NO_INSERT);
if (slot)
abort();
m->t = fndecl;
p = htab_find_slot (control_flow_table, (PTR) m, INSERT);
if (flag_create_feedback)
{
m->i = VARRAY_ACTIVE_SIZE (control_flow_to_counter_table);
VARRAY_PUSH_INT (control_flow_to_counter_table, -1);
VARRAY_PUSH_INT (control_flow_to_counter_table, -1);
VARRAY_PUSH_INT (control_flow_to_counter_table, -1);
}
else
{
m->i = control_flow_counter;
control_flow_counter += 3;
}
*p = (PTR) m;
if (flag_create_feedback)
need_func_profiler = 1;
profile_info.count_edges_instrumented_now = 0;
profile_info.count_calls_instrumented_now = 0;
(*lang_hooks.decorate_for_feedback) (fndecl, find_decoratable_tree);
slot = htab_find_slot (seen_fndecl, fndecl, INSERT);
*(tree *)slot = fndecl;
}
}
}
void
expand_function_feedback_end ()
{
if (flag_create_feedback)
{
rtx insn;
rtx next = NULL_RTX;
if (!htab_find_slot (seen_fndecl, current_function_decl, NO_INSERT))
abort();
for (insn = get_insns (); insn; insn = next)
{
next = NEXT_INSN (insn);
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_RTX)
{
emit_insn_before (NOTE_FDO_COUNT_RTX (insn), insn);
delete_insn (insn);
}
}
}
}
void
add_new_feedback_specifics_to_header (value_chain_addr, decl_chain_addr)
tree *value_chain_addr;
tree *decl_chain_addr;
{
tree value_chain = *value_chain_addr;
tree decl_chain = *decl_chain_addr;
tree field_decl;
tree value;
{
tree type_array_pointer_type
= build_pointer_type (TREE_TYPE (feedback_call_counts_table));
field_decl
= build_decl (FIELD_DECL, get_identifier ("counts2"),
build_pointer_type (make_unsigned_type (GCOV_TYPE_SIZE)));
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
if (profile_info.count_instrumented_calls)
value = build1 (ADDR_EXPR, type_array_pointer_type,
feedback_call_counts_table);
else
value = build1 (INTEGER_CST, type_array_pointer_type, 0);
value_chain = tree_cons (field_decl, value, value_chain);
}
{
field_decl
= build_decl (FIELD_DECL, get_identifier ("ncounts2"),
long_integer_type_node);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
value_chain = tree_cons (field_decl,
convert (long_integer_type_node,
build_int_2 (
profile_info.count_instrumented_calls,
0)), value_chain);
}
{
tree domain = build_index_type (build_int_2 (
VARRAY_ACTIVE_SIZE (control_flow_to_counter_table) - 1, 0));
tree array_type =
build_array_type (make_unsigned_type (LONG_TYPE_SIZE), domain);
tree type_array_pointer_type = build_pointer_type (array_type);
tree tree_to_counter_table =
build (VAR_DECL, array_type, NULL_TREE, NULL_TREE);
tree value_chain2 = NULL_TREE;
unsigned int i;
char name[20];
TREE_STATIC (tree_to_counter_table) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LFDO", 2);
DECL_NAME (tree_to_counter_table) = get_identifier (name);
DECL_SIZE (tree_to_counter_table) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (tree_to_counter_table) = TYPE_SIZE_UNIT (array_type);
DECL_ALIGN (tree_to_counter_table) = TYPE_ALIGN (array_type);
for (i = 0;
i < VARRAY_ACTIVE_SIZE (control_flow_to_counter_table);
i++)
value_chain2 = tree_cons (
build_int_2 (i, 0),
build_int_2
(VARRAY_INT (control_flow_to_counter_table, i), 0),
value_chain2);
DECL_INITIAL (tree_to_counter_table)
= build (CONSTRUCTOR, array_type, NULL_TREE,
nreverse (value_chain2));
assemble_variable (tree_to_counter_table, 0, 0, 0);
field_decl
= build_decl (FIELD_DECL, get_identifier ("counts3"),
build_pointer_type (make_unsigned_type (LONG_TYPE_SIZE)));
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
value_chain = tree_cons (field_decl, build1 (ADDR_EXPR,
type_array_pointer_type,
tree_to_counter_table),
value_chain);
}
{
field_decl
= build_decl (FIELD_DECL, get_identifier ("ncounts3"),
long_integer_type_node);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
value_chain = tree_cons (field_decl,
convert (long_integer_type_node,
build_int_2 (
VARRAY_ACTIVE_SIZE (
control_flow_to_counter_table),
0)), value_chain);
}
{
tree domain = build_index_type (build_int_2 (
VARRAY_ACTIVE_SIZE (call_to_counter_table) - 1, 0));
tree array_type =
build_array_type (make_unsigned_type (LONG_TYPE_SIZE), domain);
tree type_array_pointer_type = build_pointer_type (array_type);
tree tree_to_counter_table =
build (VAR_DECL, array_type, NULL_TREE, NULL_TREE);
tree value_chain2 = NULL_TREE;
unsigned int i;
char name[20];
TREE_STATIC (tree_to_counter_table) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LFDO", 3);
DECL_NAME (tree_to_counter_table) = get_identifier (name);
DECL_SIZE (tree_to_counter_table) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (tree_to_counter_table) = TYPE_SIZE_UNIT (array_type);
DECL_ALIGN (tree_to_counter_table) = TYPE_ALIGN (array_type);
for (i = 0;
i < VARRAY_ACTIVE_SIZE (call_to_counter_table);
i++)
value_chain2 = tree_cons (
build_int_2 (i, 0),
build_int_2
(VARRAY_INT (call_to_counter_table, i), 0),
value_chain2);
DECL_INITIAL (tree_to_counter_table)
= build (CONSTRUCTOR, array_type, NULL_TREE,
nreverse (value_chain2));
if (VARRAY_ACTIVE_SIZE (call_to_counter_table))
assemble_variable (tree_to_counter_table, 0, 0, 0);
field_decl
= build_decl (FIELD_DECL, get_identifier ("counts4"),
build_pointer_type (make_unsigned_type (LONG_TYPE_SIZE)));
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
if (VARRAY_ACTIVE_SIZE (call_to_counter_table))
value = build1 (ADDR_EXPR, type_array_pointer_type,
tree_to_counter_table);
else
value = build1 (INTEGER_CST, type_array_pointer_type, 0);
value_chain = tree_cons (field_decl, value, value_chain);
}
{
field_decl
= build_decl (FIELD_DECL, get_identifier ("ncounts4"),
long_integer_type_node);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
value_chain = tree_cons (field_decl,
convert (long_integer_type_node,
build_int_2 (
VARRAY_ACTIVE_SIZE (call_to_counter_table),
0)), value_chain);
}
*decl_chain_addr = decl_chain;
*value_chain_addr = value_chain;
}
HOST_WIDEST_INT
times_call_executed(t)
tree t;
{
int ix, j;
if (TREE_CODE (t) != CALL_EXPR)
abort ();
if (!flag_use_feedback)
return -1;
ix = find_tree_in_table (call_table, t);
if (ix == -1)
return -1;
j = VARRAY_INT (call_to_counter_table, ix);
return VARRAY_WIDEST_INT (call_counts, j);
}
void
set_times_call_executed(t, n)
tree t;
HOST_WIDEST_INT n;
{
int ix, j;
if (TREE_CODE (t) != CALL_EXPR)
abort ();
if (!flag_use_feedback)
return;
ix = find_tree_in_table (call_table, t);
if (ix != -1)
{
j = VARRAY_INT (call_to_counter_table, ix);
VARRAY_WIDEST_INT (call_counts, j) = n;
}
else
{
struct map *m = (struct map *)ggc_alloc (sizeof (struct map));
PTR* p;
m->t = t;
p = htab_find_slot (call_table, (PTR) m, INSERT);
m->i = VARRAY_ACTIVE_SIZE (call_to_counter_table);
*p = (PTR )m;
VARRAY_PUSH_INT (call_to_counter_table, VARRAY_ACTIVE_SIZE (call_counts));
VARRAY_PUSH_WIDEST_INT (call_counts, n);
}
}
HOST_WIDEST_INT
times_arc_executed (t, offset)
tree t;
unsigned int offset;
{
int ix, j;
if (!flag_use_feedback)
return -1;
ix = find_tree_in_table (control_flow_table, t);
if (ix == -1)
return -1;
j = VARRAY_INT (control_flow_to_counter_table, ix + offset);
return VARRAY_WIDEST_INT (control_flow_counts, j);
}
void
set_times_arc_executed(t, offset, n)
tree t;
unsigned int offset;
HOST_WIDEST_INT n;
{
int ix, j;
if (!flag_use_feedback)
return;
ix = find_tree_in_table (control_flow_table, t);
if (ix == -1)
{
unsigned int size = n_slots (t);
if (size > 0)
{
struct map *m = (struct map *)ggc_alloc (sizeof (struct map));
PTR* p;
m->t = t;
p = htab_find_slot (control_flow_table, (PTR) m, INSERT);
m->i = ix = VARRAY_ACTIVE_SIZE (control_flow_to_counter_table);
*p = (PTR )m;
for (; size > 0; size--)
{
VARRAY_PUSH_INT (control_flow_to_counter_table,
VARRAY_ACTIVE_SIZE (control_flow_counts));
VARRAY_PUSH_WIDEST_INT (control_flow_counts, -1);
}
}
}
j = VARRAY_INT (control_flow_to_counter_table, ix + offset);
VARRAY_WIDEST_INT (control_flow_counts, j) = n;
}
struct edge_info {
unsigned int count_valid : 1;
unsigned int on_tree : 1;
unsigned int ignore : 1;
};
struct bb_info {
unsigned int count_valid : 1;
gcov_type succ_count;
gcov_type pred_count;
};
#define EDGE_INFO(e) ((struct edge_info *) (e)->aux)
#define BB_INFO(b) ((struct bb_info *) (b)->aux)
void
note_feedback_count_to_br_prob ()
{
basic_block bb;
int changes;
int passes;
int hist_br_prob[20];
int num_never_executed;
int num_branches;
int roundoff_err_found;
int n_edges;
int max_n_edges = 0;
int i;
rtx insn;
if (!flag_use_feedback)
return;
add_noreturn_fake_exit_edges ();
connect_infinite_loops_to_exit ();
alloc_aux_for_blocks (sizeof (struct bb_info));
alloc_aux_for_edges (sizeof (struct edge_info));
FOR_EACH_BB (bb)
{
edge e;
for (e = bb->succ; e; e = e->succ_next)
BB_INFO (bb)->succ_count++;
for (e = bb->pred; e; e = e->pred_next)
BB_INFO (bb)->pred_count++;
}
FOR_EACH_BB (bb)
{
edge e;
gcov_type in_block_count = -1;
gcov_type out_block_count = -1;
gcov_type block_count = -1;
rtx insn;
for (insn = bb->head; insn != NEXT_INSN (bb->end);
insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_INCOMING)
{
int count = 0;
if (in_block_count != -1)
{
if (get_fdo_note_count (insn) != in_block_count)
abort ();
#if 0
warning ("Duplicate COUNT_INCOMING note.");
#endif
continue;
}
for (e = bb->pred; e; e = e->pred_next)
count++;
if (count != 1)
warning("COUNT_INCOMING in block with multiple predecessors.");
bb->pred->count = in_block_count = get_fdo_note_count (insn);
EDGE_INFO (bb->pred)->count_valid = 1;
BB_INFO (bb)->pred_count--;
BB_INFO (bb->pred->src)->succ_count--;
delete_insn (insn);
}
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_OUTGOING)
{
int count = 0;
edge real_exit = 0;
if (out_block_count != -1)
{
if (get_fdo_note_count (insn) != out_block_count)
abort ();
warning ("Duplicate COUNT_OUTGOING note.");
continue;
}
for (e = bb->succ; e; e = e->succ_next)
if (e->flags & EDGE_FAKE)
{
BB_INFO (bb)->succ_count--;
e->count = 0;
EDGE_INFO (e)->count_valid = 1;
}
else
{
count++;
real_exit = e;
}
if (count != 1)
warning("COUNT_OUTGOING in block with multiple successors.");
out_block_count = get_fdo_note_count (insn);
real_exit->count = out_block_count = get_fdo_note_count (insn);
EDGE_INFO (real_exit)->count_valid = 1;
BB_INFO (bb)->succ_count--;
BB_INFO (real_exit->dest)->pred_count--;
delete_insn (insn);
}
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_TABLEJUMP)
{
rtvec vec = NOTE_FDO_TABLEJUMP (insn);
int i;
for ( i=0; i < GET_NUM_ELEM (vec); i += 2)
{
rtx label = XEXP (RTVEC_ELT (vec, i), 0);
rtx count = RTVEC_ELT (vec, i+1);
HOST_WIDEST_INT value = INTVAL (count);
for (e = bb->succ; e; e = e->succ_next)
if (e->dest->head == label)
{
if (EDGE_INFO (e)->count_valid)
{
if (e->count != value)
warning ("Duplicate TABLEJUMP counts don't match!"); }
else
{
BB_INFO (bb)->succ_count--;
BB_INFO (e->dest)->pred_count--;
}
EDGE_INFO (e)->count_valid = 1;
e->count = value;
break;
}
}
delete_insn (insn);
}
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_BLOCK)
{
if (block_count != -1)
{
if (get_fdo_note_count (insn) != block_count)
{
if (get_fdo_note_count (insn) == -1)
continue;
abort ();
}
continue;
}
bb->count = block_count = get_fdo_note_count (insn);
BB_INFO (bb)->count_valid = 1;
delete_insn (insn);
}
}
if (in_block_count != -1 && out_block_count != -1
&& in_block_count != out_block_count
&& in_block_count != out_block_count + 1)
warning("INCOMING and OUTGOING counts do not agree!");
if (in_block_count != -1 && block_count != -1
&& in_block_count != block_count
&& in_block_count != block_count + 1)
warning("INCOMING and BLOCK counts do not agree!");
if (block_count != -1 && out_block_count != -1
&& block_count != out_block_count
&& block_count != out_block_count + 1)
warning("BLOCK and OUTGOING counts do not agree!");
}
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_INCOMING
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_OUTGOING
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_BLOCK
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_FDO_COUNT_TABLEJUMP))
delete_insn (insn);
changes = 1;
passes = 0;
roundoff_err_found = 0;
while (changes)
{
passes++;
changes = 0;
FOR_EACH_BB (bb)
{
struct bb_info *bi = BB_INFO (bb);
if (! bi->count_valid)
{
if (bi->succ_count == 0)
{
edge e;
gcov_type total = 0;
for (n_edges = 0, e = bb->succ; e; e = e->succ_next)
{
total += e->count;
n_edges++;
}
bb->count = total;
bi->count_valid = 1;
changes = 1;
if (n_edges > max_n_edges)
max_n_edges = n_edges;
}
else if (bi->pred_count == 0)
{
edge e;
gcov_type total = 0;
for (n_edges = 0, e = bb->pred; e; e = e->pred_next)
{
total += e->count;
n_edges++;
}
bb->count = total;
bi->count_valid = 1;
changes = 1;
if (n_edges > max_n_edges)
max_n_edges = n_edges;
}
}
if (bi->count_valid)
{
if (bi->succ_count == 1)
{
edge e, remaining_e = 0;
gcov_type total = 0;
for (n_edges = 0, e = bb->succ; e; e = e->succ_next)
{
n_edges++;
if (!EDGE_INFO (e)->count_valid)
remaining_e = e;
else
total += e->count;
}
if (n_edges > max_n_edges)
max_n_edges = n_edges;
total = bb->count - total;
if (total >= -(passes == 1 ? n_edges : max_n_edges)
&& total <= -1)
{
total = 0;
roundoff_err_found = 1;
}
if (! remaining_e)
abort ();
EDGE_INFO (remaining_e)->count_valid = 1;
remaining_e->count = total;
bi->succ_count--;
BB_INFO (remaining_e->dest)->pred_count--;
changes = 1;
}
if (bi->pred_count == 1)
{
edge e, remaining_e = 0;
gcov_type total = 0;
for (n_edges = 0, e = bb->pred; e; e = e->pred_next)
{
n_edges++;
if (!EDGE_INFO (e)->count_valid)
remaining_e = e;
else
total += e->count;
}
if (n_edges > max_n_edges)
max_n_edges = n_edges;
total = bb->count - total;
if (! remaining_e)
abort ();
if (total >= -(passes == 1 ? n_edges : max_n_edges)
&& total <= -1)
{
total = 0;
roundoff_err_found = 1;
}
EDGE_INFO (remaining_e)->count_valid = 1;
remaining_e->count = total;
bi->pred_count--;
BB_INFO (remaining_e->src)->succ_count--;
changes = 1;
}
}
}
}
if (rtl_dump_file)
{
fprintf (rtl_dump_file, "Graph solving took %d passes.\n\n", passes);
}
FOR_EACH_BB (bb)
{
if (BB_INFO (bb)->succ_count || BB_INFO (bb)->pred_count)
warning("Unable to compute execution counts!");
}
for (i = 0; i < 20; i++)
hist_br_prob[i] = 0;
num_never_executed = 0;
num_branches = 0;
FOR_EACH_BB (bb)
{
edge e;
gcov_type total;
rtx note, insn;
if (!BB_INFO (bb)->count_valid)
abort ();
total = bb->count;
for (insn = bb->head; ; insn = NEXT_INSN (insn))
{
if (NOTE_INSN_BASIC_BLOCK_P (insn))
{
note = emit_note_after (NOTE_INSN_FDO_COUNT_BLOCK, insn);
set_fdo_note_count (note, total);
break;
}
if (insn == bb->end)
break;
}
for (e = bb->succ; e; e = e->succ_next)
{
if (!EDGE_INFO (e)->count_valid)
abort ();
if (e->count - total >= 1 && e->count - total <= max_n_edges)
total = e->count;
e->probability = (e->count * REG_BR_PROB_BASE + total / 2) / total;
if (e->probability < 0 || e->probability > REG_BR_PROB_BASE)
{
error ("corrupted profile info: prob for %d-%d thought to be %d",
e->src->index, e->dest->index, e->probability);
e->probability = REG_BR_PROB_BASE / 2;
}
}
if (bb->index >= 0
&& any_condjump_p (bb->end)
&& bb->succ->succ_next)
{
int prob;
edge e;
int index;
for (e = bb->succ; e->flags & (EDGE_FAKE | EDGE_FALLTHRU);
e = e->succ_next)
continue;
prob = e->probability;
index = prob * 20 / REG_BR_PROB_BASE;
if (index == 20)
index = 19;
hist_br_prob[index]++;
note = find_reg_note (bb->end, REG_BR_PROB, 0);
if (note)
XEXP (note, 0) = GEN_INT (prob);
else
REG_NOTES (bb->end)
= gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (prob),
REG_NOTES (bb->end));
num_branches++;
}
}
if (rtl_dump_file)
{
fprintf (rtl_dump_file, "%d branches\n", num_branches);
fprintf (rtl_dump_file, "%d branches never executed\n",
num_never_executed);
if (num_branches)
for (i = 0; i < 10; i++)
fprintf (rtl_dump_file, "%d%% branches in range %d-%d%%\n",
(hist_br_prob[i] + hist_br_prob[19-i]) * 100 / num_branches,
5 * i, 5 * i + 5);
fputc ('\n', rtl_dump_file);
fputc ('\n', rtl_dump_file);
}
remove_fake_edges ();
free_aux_for_edges ();
free_aux_for_blocks ();
}
#include "gt-feedback.h"