/* Sample profile support in GCC.
Copyright (C) 2008
Free Software Foundation, Inc.
Contributed by Paul Yuan (yingbo.com@gmail.com)
and Vinodha Ramasamy (vinodha@google.com)
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
/* References:
[1] "Feedback-directed Optimizations in GCC with Estimated Edge Profiles
from Hardware Event Sampling", Vinodha Ramasamy, Paul Yuan, Dehao Chen,
and Robert Hundt; GCC Summit 2008.
*/
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "hashtab.h"
#include "rtl.h"
#include "expr.h"
#include "basic-block.h"
#include "output.h"
#include "flags.h"
#include "langhooks.h"
#include "recog.h"
#include "optabs.h"
#include "ggc.h"
#include "tree-flow.h"
#include "diagnostic.h"
#include "coverage.h"
#include "tree.h"
#include "gcov-io.h"
#include "cgraph.h"
#include "cfgloop.h"
#include "timevar.h"
#include "tree-pass.h"
#include "toplev.h"
#include "params.h"
#include "gimple.h"
#include "profile.h"
#include "tree-sample-profile.h"
#include "opts.h"
#include "l-ipo.h"
#include "value-prof.h"
#define DEFAULT_SAMPLE_DATAFILE "sp.data"
#define MAX_LINENUM_CHARS 10
#define FB_INLINE_MAX_STACK 200
#define MAX_LINES_PER_BASIC_BLOCK 500
#define MIN_SAMPLE_BB_COUNT 5
#define DISCRIM(x) (PARAM_VALUE (PARAM_SAMPLEFDO_USE_DISCRIMINATORS) ? (x) : 0)
/* File name of sample file. */
const char *sample_data_name = NULL;
/* Hashtable to hold elements with
from sample file. */
static htab_t sp_htab;
/* Buffer to hold elements inserted into sp_htab. */
struct sample_freq_detail *sample_buf;
/* Hashtable to hold elements for inlined function samples with
.
format:
:::
::...
:. */
static htab_t sp_inline_htab;
static htab_t sp_indirect_htab;
static htab_t sp_funcname_htab;
/* Buffer to hold elements inserted into sp_inline_htab. */
struct sample_inline_freq *inline_sample_buf;
/* Number of samples read from sample file. */
static unsigned long long sp_num_samples;
/* Maximum count/freq in the sample file. */
static gcov_type sp_max_count;
/* Assist for the reading of sample file. */
static struct profile prog_unit;
static struct gcov_ctr_summary *sp_profile_info;
/* Print hash table statistics for HTAB. */
static void
print_hash_table_statistics (htab_t htab)
{
if (!dump_file)
return;
fprintf (dump_file,
"sample_profile hash - size: %ld, elements %ld, collisions: %f\n",
(long) htab_size (htab), (long) htab_elements (htab),
htab_collisions (htab));
}
/* Dump CFG profile information into output file named PNAME. File format:
**********************************************
;;n_basic_blocks n_edges count function_name1
e1->src->index e1->dest->index pw probability count
...
;;n_basic_blocks n_edges count function_name1
e1->src->index e1->dest->index pw probability count
...
...
**********************************************
pw (percentage weight) is a metric for overlap measurement. */
static void
dump_cfg_profile (const char *pname)
{
FILE *prof_compare_file;
basic_block bb;
edge e;
edge_iterator ei;
/* Sum of edge frequencies. */
int sum_edge_freq = 0;
prof_compare_file = fopen (pname, "a");
if (!prof_compare_file)
{
inform (0, "Cannot create output file %s to dump CFG profile", pname);
return;
}
fprintf (prof_compare_file, ";;%d %d " HOST_WIDEST_INT_PRINT_DEC " %s\n",
n_basic_blocks, n_edges, ENTRY_BLOCK_PTR->count,
lang_hooks.decl_printable_name (current_function_decl, 2));
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
FOR_EACH_EDGE (e, ei, bb->succs)
sum_edge_freq += e->src->frequency * e->probability / REG_BR_PROB_BASE;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
{
FOR_EACH_EDGE (e, ei, bb->succs)
{
int efreq = e->src->frequency * e->probability / REG_BR_PROB_BASE;
if (sum_edge_freq)
fprintf (prof_compare_file,
"%d %d %f %d " HOST_WIDEST_INT_PRINT_DEC "\n", bb->index,
e->dest->index, (float) efreq / sum_edge_freq,
e->probability, e->count);
else
fprintf (prof_compare_file,
"%d %d 0.0 %d " HOST_WIDEST_INT_PRINT_DEC "\n", bb->index,
e->dest->index, e->probability, e->count);
}
}
fclose (prof_compare_file);
}
/* extract the path info of NAME, and return the file name. */
static const char *realname (const char *name) {
const char *c;
for (c = name; *c; c++) {
if (*c == '/') name = c+1;
}
return name;
}
static hashval_t
sp_indirect_hash (const void *fb_info)
{
const struct sample_indirect_call *si =
(const struct sample_indirect_call *) fb_info;
hashval_t h = 0x9e3779b9;
return iterative_hash (si->func_name, strlen (si->func_name), h);
}
static int
sp_indirect_eq (const void *p, const void *q)
{
const struct sample_indirect_call *a =
(const struct sample_indirect_call *) p;
const struct sample_indirect_call *b =
(const struct sample_indirect_call *) q;
return !strcmp(a->func_name, b->func_name);
}
/* Functions used for hash table to store samples.
key = string base_filename:line_num. */
/* Create a hash string with FILENAME, LINE_NUM, DISCRIMINATOR, and
FUNCNAME. */
static hashval_t
create_hash_string (const char *filename, int line_num, int discriminator)
{
/* An arbitrary initial value borrowed from hashtab.c. */
hashval_t h = 0x9e3779b9;
filename = realname (filename);
h = iterative_hash (filename, strlen (filename), h);
h = iterative_hash (&line_num, sizeof (line_num), h);
h = iterative_hash (&discriminator, sizeof(discriminator), h);
return h;
}
/* Hash function for struct sample_freq_detail entry. */
static hashval_t
sp_info_hash (const void *fb_info)
{
const struct sample_freq_detail *sp =
(const struct sample_freq_detail *) fb_info;
gcc_assert (sp->line_num >= 0);
return create_hash_string (realname (sp->filename), sp->line_num,
sp->discriminator);
}
/* Check if two elements of type sample_freq_detail pointed to by P and Q are
equal. */
static int
sp_info_eq (const void *p, const void *q)
{
const struct sample_freq_detail *a =
(const struct sample_freq_detail *) p;
const struct sample_freq_detail *b =
(const struct sample_freq_detail *) q;
return (a->line_num == b->line_num)
&& (a->discriminator == b->discriminator)
&& (!strcmp (realname (a->filename), realname (b->filename)));
}
/* Compute hash value for INLINE_INFO. */
static hashval_t
sp_inline_info_hash (const void *inline_info)
{
/* An arbitrary initial value borrowed from hashtab.c. */
hashval_t h = 0x9e3779b9;
const struct sample_inline_freq *i_info =
(const struct sample_inline_freq *) inline_info;
int depth = i_info->depth;
int i = 0;
while (i < depth)
{
h = iterative_hash (realname (i_info->inline_stack[i].loc.file),
strlen (realname (i_info->inline_stack[i].loc.file)),
h);
h = iterative_hash (&(i_info->inline_stack[i].loc.line),
sizeof (i_info->inline_stack[i].loc.line), h);
i++;
}
h = iterative_hash (realname (i_info->filename),
strlen (realname (i_info->filename)), h);
h = iterative_hash (&(i_info->line_num), sizeof (i_info->line_num), h);
h = iterative_hash (&(i_info->discriminator),
sizeof (i_info->discriminator), h);
return h;
}
/* Return non-zero if the two sample_inline_freq elements pointed to by P and
Q are equal, 0 otherwise. */
static int
sp_inline_info_eq (const void *p, const void *q)
{
const struct sample_inline_freq *a =
(const struct sample_inline_freq *) p;
const struct sample_inline_freq *b =
(const struct sample_inline_freq *) q;
int i = 0;
if (a->line_num != b->line_num)
return 0;
if (a->discriminator != b->discriminator)
return 0;
/* Compare the inline stacks. */
if (a->depth != b->depth)
return 0;
while (i < a->depth)
{
if ((a->inline_stack[i].loc.line != b->inline_stack[i].loc.line)
|| strcmp (realname (a->inline_stack[i].loc.file),
realname (b->inline_stack[i].loc.file)))
return 0;
i++;
}
return !strcmp (realname (a->filename), realname (b->filename));
}
/* Usage model: All elements in the hash table are deleted only at time of hash
table deletion. INLINE_STACK is shared among multiple elements, so use the
IS_FIRST field to determine when to free it. Also, if the num of value
profiles are more than 0, free the memory used to store value profile. */
static void
sp_inline_info_del (void *p)
{
struct sample_inline_freq *a = (struct sample_inline_freq *) p;
if (a->is_first)
free (a->inline_stack);
if (a->num_value > 0)
free (a->values);
}
/* Free the memory of value profile if there are any. */
static void
sp_info_del (void *p)
{
struct sample_freq_detail *a = (struct sample_freq_detail *) p;
if (a->num_value > 0)
free (a->values);
}
/* Structure to store the mapping between the new function name and the
original function name. */
#define MAX_FUNC_NAME_LENGTH 2048
struct name_mapping
{
char new_name[MAX_FUNC_NAME_LENGTH];
char orig_name[MAX_FUNC_NAME_LENGTH];
};
/* Compute Hash Value for function name. The hash is only computed on the new
function name. */
static hashval_t
sp_funcname_hash (const void *name)
{
const struct name_mapping *mapping =
(const struct name_mapping *)name;
hashval_t h = 0x9e3779b9;
h = iterative_hash (mapping->new_name,
strlen (mapping->new_name), h);
return h;
}
/* Return non-Zero if the function names are equal. Only the new name is
compared here. */
static int
sp_funcname_eq (const void *p, const void *q)
{
const struct name_mapping *m1 = (const struct name_mapping *)p;
const struct name_mapping *m2 = (const struct name_mapping *)q;
return !strcmp(m1->new_name, m2->new_name);
}
/* Array to store the function name mapping.
TODO: This is ugly... however when I use xmalloc, it generate the random
error... Need to be converted to dynamic memory allocation later. */
#define MAX_FUNC_MAPPINGS 65536
static struct name_mapping funcname_buf[MAX_FUNC_MAPPINGS];
static int funcname_buf_size = 0;
/* External function to add a mapping between function names. */
void sp_add_funcname_mapping (const char *new_name, const char *orig_name)
{
struct name_mapping **slot;
strcpy(funcname_buf[funcname_buf_size].new_name, new_name);
strcpy(funcname_buf[funcname_buf_size].orig_name, orig_name);
slot = (struct name_mapping **)
htab_find_slot (sp_funcname_htab, &funcname_buf[funcname_buf_size], INSERT);
if (!*slot)
{
gcc_assert (funcname_buf_size < MAX_FUNC_MAPPINGS);
*slot = &funcname_buf[funcname_buf_size++];
}
else
{
inform (0, "Duplicate entry of function name mapping");
}
}
/* External function to get the original function name either through name
itself or through name mapping. */
const char *sp_get_real_funcname (const char *name)
{
struct name_mapping nm, *pnm;
if (!flag_dyn_ipa)
return name;
strcpy (nm.new_name, name);
pnm = (struct name_mapping *)
htab_find (sp_funcname_htab, (void *) &nm);
if (!pnm)
return name;
else
return pnm->orig_name;
}
/* Get and store the inline stack corresponding to STMT into the output
parameter STACK. */
static int
sp_get_inline_stack (gimple stmt, struct expanded_inline_location *stack)
{
tree block = gimple_block (stmt);
unsigned int i = 0, last_loc = 0;
if (!block || (TREE_CODE (block) != BLOCK))
return 0;
for ( block = BLOCK_SUPERCONTEXT (block);
block && (TREE_CODE (block) == BLOCK);
block = BLOCK_SUPERCONTEXT (block)) {
if (!BLOCK_SOURCE_LOCATION (block) > 0
|| BLOCK_SOURCE_LOCATION (block) == last_loc)
continue;
last_loc = BLOCK_SOURCE_LOCATION (block);
stack[i].loc = expand_location (last_loc);
stack[i++].discriminator = get_discriminator_from_locus (last_loc);
}
return i;
}
/* Build a hashtab element to state that FUNC_NAME has an indirect
call edge of weight COUNT to the TARGET function. */
static void
sp_add_indirect_call (const char *func_name, const char *target,
gcov_type count)
{
struct sample_indirect_call ic;
struct sample_indirect_call *entry;
struct sample_indirect_call **slot;
unsigned i;
ic.func_name = func_name;
slot = (struct sample_indirect_call **)
htab_find_slot (sp_indirect_htab, (void *) &ic, INSERT);
if (!*slot) {
*slot = (struct sample_indirect_call *)
xmalloc(sizeof(struct sample_indirect_call));
(*slot)->func_name = func_name;
(*slot)->num_values = 0;
}
entry = *slot;
if (entry->num_values == MAX_IND_FUNCS)
return;
for (i = 0; i < entry->num_values; i++) {
if (!strcmp(target, entry->targets[i])) {
entry->count[i] += count;
return;
}
}
entry->targets[entry->num_values] = 0;
entry->targets[entry->num_values++] = target;
}
/* Read in the FUNC_NAME, return the hashtab that contains the
indirect calls from this function. */
struct sample_indirect_call *
sp_get_indirect_calls (const char *func_name)
{
struct sample_indirect_call ic;
ic.func_name = func_name;
return (struct sample_indirect_call *)
htab_find (sp_indirect_htab, (void *) &ic);
}
/* Read file header from input file INFILE into PROG_UNIT. Return 0 if
successful, -1 otherwise. */
static int
read_file_header (FILE *infile, struct profile *prog_unit)
{
if (fread (&(prog_unit->fb_hdr), 1, sizeof (struct fb_sample_hdr), infile)
!= sizeof (struct fb_sample_hdr))
return -1;
return 0;
}
/* Read string table from INFILE into PROG_UNIT. Return 0 if successful, -1
otherwise. */
static int
read_string_table (FILE *infile, struct profile *prog_unit)
{
unsigned long long str_table_size;
if (fseek (infile, (prog_unit->fb_hdr).fb_str_table_offset, SEEK_SET) != 0)
return -1;
str_table_size = prog_unit->fb_hdr.fb_str_table_size;
prog_unit->str_table = (char *) xmalloc (str_table_size);
if (!(prog_unit->str_table))
return -1;
if (fread (prog_unit->str_table, 1, str_table_size, infile) !=
str_table_size)
return -1;
return 0;
}
/* Read function header with index I from INFILE into FUNC_HDR. PROG_UNIT holds
file header and string table. Return 0 if successful, -1 otherwise. */
static int
read_function_header (FILE *infile, unsigned int i,
struct profile *prog_unit,
struct func_sample_hdr *func_hdr)
{
struct fb_sample_hdr *fb_hdr = &(prog_unit->fb_hdr);
unsigned int func_hdr_size;
unsigned int offset;
gcc_assert (i <= fb_hdr->fb_func_hdr_num);
func_hdr_size = fb_hdr->fb_func_hdr_ent_size;
offset = i * func_hdr_size;
if (fseek (infile, fb_hdr->fb_func_hdr_offset + offset, SEEK_SET) != 0)
return -1;
if (fread (func_hdr, 1, func_hdr_size, infile) != func_hdr_size)
return -1;
return 0;
}
static int
get_discriminator (gimple stmt)
{
location_t loc = gimple_location (stmt);
if (loc == UNKNOWN_LOCATION)
return -1;
return get_discriminator_from_locus (loc);
}
/* Get the inline stack of STMT, and write it to the STACK with offset of IDX.
Return the new offset. Returns -1 on error. */
static int
get_stack_trace_by_stmt (gimple stmt, struct expanded_inline_location *stack,
int idx)
{
tree block;
unsigned int last_loc = 0;
if (!stmt)
return -1;
block = gimple_block (stmt);
if (!block || TREE_CODE (block) != BLOCK || !gimple_location(stmt))
return -1;
stack[idx].loc = expand_location (gimple_location (stmt));
stack[idx++].discriminator = get_discriminator(stmt);
for ( block = BLOCK_SUPERCONTEXT (block);
block && (TREE_CODE (block) == BLOCK);
block = BLOCK_SUPERCONTEXT (block)) {
if (! BLOCK_SOURCE_LOCATION (block) > 0 ||
BLOCK_SOURCE_LOCATION (block) == last_loc)
continue;
last_loc = BLOCK_SOURCE_LOCATION (block);
gcc_assert (idx < FB_INLINE_MAX_STACK);
stack[idx].loc = expand_location (last_loc);
stack[idx++].discriminator = get_discriminator_from_locus(last_loc);
}
return idx;
}
/* Get the total execution count of an inlined function. */
unsigned long long
get_total_count (gimple stmt, const char *func_name)
{
int idx = 0;
struct expanded_inline_location stack[FB_INLINE_MAX_STACK];
struct sample_inline_freq inline_loc;
struct sample_inline_freq *inline_htab_entry;
idx = get_stack_trace_by_stmt (stmt, stack, 0);
if (idx == -1)
return 0;
inline_loc.depth = idx;
inline_loc.inline_stack = stack;
inline_loc.func_name = func_name;
inline_loc.filename = "";
inline_loc.line_num = 0;
inline_loc.discriminator = 0;
inline_htab_entry = (struct sample_inline_freq *)
htab_find (sp_inline_htab, (void *) &inline_loc);
if (!inline_htab_entry)
return 0;
return inline_htab_entry->freq;
}
/* Get the total execution count of an inlined function. */
unsigned long long
get_total_count_edge (struct cgraph_edge *edge, const char *func_name)
{
int idx = 0;
gimple stmt = edge->call_stmt;
struct expanded_inline_location stack[FB_INLINE_MAX_STACK];
struct sample_inline_freq inline_loc;
struct sample_inline_freq *inline_htab_entry;
idx = get_stack_trace_by_stmt (stmt, stack, 0);
if (idx == -1)
return 0;
while (edge->caller->global.inlined_to)
{
edge = edge->caller->callers;
stmt = edge->call_stmt;
idx = get_stack_trace_by_stmt (stmt, stack, idx);
if (idx == -1)
return 0;
}
inline_loc.depth = idx;
inline_loc.inline_stack = stack;
inline_loc.func_name = func_name;
inline_loc.filename = "";
inline_loc.line_num = 0;
inline_loc.discriminator = 0;
inline_htab_entry = (struct sample_inline_freq *)
htab_find (sp_inline_htab, (void *) &inline_loc);
if (!inline_htab_entry)
return 0;
return inline_htab_entry->freq;
}
/* Read inline sections for the function header FUNC_HDR in file INFILE.
PROG_UNIT holds file header information and string table. Input parameter
NUM_SAMPLES specifies the number of samples read so far. Return the
total number of samples read, including NUM_SAMPLES. */
static unsigned long long
read_inline_function (FILE *infile, struct profile *prog_unit,
struct func_sample_hdr *func_hdr,
unsigned long long num_samples)
{
unsigned long long inline_hdr_offset, profile_offset;
unsigned long long j, k;
unsigned long long num_lines;
struct func_sample_hdr inline_func_hdr;
struct fb_sample_hdr *fb_hdr = &(prog_unit->fb_hdr);
unsigned int func_hdr_size = fb_hdr->fb_func_hdr_ent_size;
unsigned long long num_inlines = func_hdr->func_num_inline_entries;
unsigned long long curr_num_samples = num_samples;
for (k = 0; k < num_inlines; k++)
{
int i = 0;
struct expanded_inline_location *stack_buf;
struct fb_info_inline_stack_entry stack_entry[FB_INLINE_MAX_STACK];
size_t stack_entry_size = sizeof (struct fb_info_inline_stack_entry);
int inline_depth;
struct sample_inline_freq **slot;
inline_hdr_offset =
fb_hdr->fb_func_hdr_offset + func_hdr->func_inline_hdr_offset
+ fb_hdr->fb_func_hdr_num * func_hdr_size + k * func_hdr_size;
if (fseek (infile, inline_hdr_offset, SEEK_SET) != 0)
{
error ("read_inline_function(): fseek inline_func_hdr error.");
return curr_num_samples;
}
if (fread (&inline_func_hdr, 1, func_hdr_size, infile) != func_hdr_size)
{
error ("read_inline_function(): fread inline_func_hdr error.");
return curr_num_samples;
}
inline_depth = inline_func_hdr.inline_depth;
num_lines = inline_func_hdr.func_num_freq_entries;
if (num_lines == 0)
continue;
profile_offset = prog_unit->fb_hdr.fb_profile_offset
+ inline_func_hdr.inline_stack_offset;
stack_buf = (struct expanded_inline_location *)
xcalloc (inline_func_hdr.inline_depth,
sizeof (struct expanded_inline_location));
/* Seek to beginning of the inline stack. */
if (fseek (infile, profile_offset, SEEK_SET) != 0)
{
error ("read_inline_function(): fseek profile_data error.");
return curr_num_samples;
}
gcc_assert (inline_depth < FB_INLINE_MAX_STACK);
gcc_assert (inline_depth > 0);
if (fread (&stack_entry, stack_entry_size, inline_depth, infile)
!= (size_t) inline_depth)
{
error ("read_inline_function(): fread profile_data error.");
return curr_num_samples;
}
/* Set up stack buffer. */
i = 0;
while (i < inline_depth)
{
/* Stack stored in reverse in datafile. */
stack_buf[inline_depth - i - 1].loc.file =
&(prog_unit->str_table[stack_entry[i].filename_offset]);
stack_buf[inline_depth - i - 1].loc.line = stack_entry[i].line_num;
stack_buf[inline_depth - i - 1].discriminator
= stack_entry[i].discriminator;
i++;
}
/* The last entry in inline_sample_buf presents the total sample of the
inlined function. */
inline_sample_buf = (struct sample_inline_freq *)
xcalloc (num_lines + 1, sizeof (struct sample_inline_freq));
/* Insert the total sample of the callsite */
inline_sample_buf[num_lines].func_name =
&(prog_unit->str_table[inline_func_hdr.func_name_index]);
inline_sample_buf[num_lines].depth = inline_depth;
inline_sample_buf[num_lines].inline_stack = stack_buf;
inline_sample_buf[num_lines].filename = "";
inline_sample_buf[num_lines].line_num = 0;
inline_sample_buf[num_lines].discriminator = 0;
inline_sample_buf[num_lines].freq = inline_func_hdr.total_samples;
inline_sample_buf[num_lines].is_first = false;
/* Insert new sample into inline hash table. */
slot = (struct sample_inline_freq **)
htab_find_slot (sp_inline_htab, &inline_sample_buf[num_lines], INSERT);
if (*slot)
inform (0, "Duplicate entry of callstack");
else
*slot = &inline_sample_buf[num_lines];
for (j = 0; j < num_lines; ++j)
{
struct fb_info_freq sample;
/* Find the offset of the frequency profile. */
profile_offset = prog_unit->fb_hdr.fb_profile_offset
+ inline_func_hdr.func_profile_offset + j * sizeof (sample);
if (fseek (infile, profile_offset, SEEK_SET) != 0)
{
error ("read_inline_function(): fseek profile_data error.");
return curr_num_samples;
}
if (fread (&sample, 1, sizeof (sample), infile) != sizeof (sample))
{
error ("read_inline_function(): fread profile_data error.");
return curr_num_samples;
}
inline_sample_buf[j].func_name =
&(prog_unit->str_table[inline_func_hdr.func_name_index]);
inline_sample_buf[j].depth = inline_depth;
inline_sample_buf[j].inline_stack = stack_buf;
inline_sample_buf[j].filename =
&(prog_unit->str_table[sample.filename_offset]);
inline_sample_buf[j].line_num = sample.line_num;
inline_sample_buf[j].discriminator = DISCRIM (sample.discriminator);
inline_sample_buf[j].freq = sample.freq;
inline_sample_buf[j].num_instr = sample.num_instr;
inline_sample_buf[j].num_value = sample.num_value;
/* Read in the value profiles if available. */
if (sample.num_value > 0) {
unsigned long long k;
struct fb_info_hist *hist_buf = (struct fb_info_hist *)
alloca (sample.num_value * sizeof (struct fb_info_hist));
/* Find the offset of the value profile. */
profile_offset = prog_unit->fb_hdr.fb_profile_offset
+ inline_func_hdr.func_hist_table_offset
+ sample.hist_offset * sizeof(struct fb_info_hist);
if (fseek (infile, profile_offset, SEEK_SET) != 0)
{
error ("read_inline_function(): fseek profile_data error.");
return curr_num_samples;
}
if (fread (hist_buf,
sample.num_value,
sizeof (struct fb_info_hist),
infile) != sizeof (struct fb_info_hist))
{
error ("read_inline_function(): fread profile_data error.");
return curr_num_samples;
}
inline_sample_buf[j].values = (struct sample_hist *)
xcalloc (sample.num_value, sizeof (struct sample_hist));
for (k = 0; k < sample.num_value; k++) {
inline_sample_buf[j].values[k].type = hist_buf[k].type;
if (hist_buf[k].type == CALL_HIST)
{
inline_sample_buf[j].values[k].value.func_name =
&(prog_unit->str_table[hist_buf[k].value]);
sp_add_indirect_call (
&(prog_unit->str_table[func_hdr->func_name_index]),
&(prog_unit->str_table[hist_buf[k].value]),
hist_buf[k].count);
}
else
{
inline_sample_buf[j].values[k].value.value = hist_buf[k].value;
}
inline_sample_buf[j].values[k].count = hist_buf[k].count;
}
}
/* All the entries share the inline_stack. Mark the first entry to
track when to delete the inline_stack. */
if (j == 0)
inline_sample_buf[j].is_first = true;
else
inline_sample_buf[j].is_first = false;
if (sample.freq > sp_max_count)
sp_max_count = sample.freq;
/* Insert new sample into inline hash table. */
slot = (struct sample_inline_freq **)
htab_find_slot (sp_inline_htab, &inline_sample_buf[j], INSERT);
if (*slot)
{
if (PARAM_VALUE (PARAM_SAMPLEFDO_USE_DISCRIMINATORS))
{
inform (0, "Duplicate entry: %s:%d",
inline_sample_buf[j].filename,
inline_sample_buf[j].line_num);
}
else
{
/* When not using discriminators, merge multiple
entries with different discriminator values */
(*slot)->freq += inline_sample_buf[j].freq;
(*slot)->num_instr += inline_sample_buf[j].num_instr;
}
}
else
{
*slot = &inline_sample_buf[j];
curr_num_samples++;
}
}
}
return curr_num_samples;
}
/* Rountine that returns the total number of option string entries in the
module specified by the header HDR. */
static unsigned long long
sp_module_num_strings (const struct module_sample_hdr *hdr)
{
return hdr->num_quote_paths +
hdr->num_bracket_paths +
hdr->num_cpp_defines +
hdr->num_cpp_includes +
hdr->num_cl_args;
}
/* Routine that reads in the option info (stored in PROFILE, and header HDR),
and update the MODULE_INFO. Use PROG_UNIT to get the string table. If the
module is primary module (as specified by IS_PRIMARY), set the flags
accordingly. */
static void
sp_add_module (struct gcov_module_info **module_info,
struct fb_info_module *profile,
struct module_sample_hdr *hdr,
gcov_unsigned_t is_primary,
struct profile *prog_unit)
{
unsigned int i, current_offset = 0;
size_t info_sz;
info_sz = sizeof (struct gcov_module_info) +
sizeof (void *) * sp_module_num_strings (hdr);
*module_info = XCNEWVAR (struct gcov_module_info, info_sz);
(*module_info)->ident = hdr->module_name_index;
(*module_info)->is_primary = is_primary;
(*module_info)->is_exported = is_primary ? hdr->exported : 1;
(*module_info)->source_filename
= &(prog_unit->str_table[hdr->module_name_index]);
(*module_info)->num_quote_paths = hdr->num_quote_paths;
(*module_info)->num_bracket_paths = hdr->num_bracket_paths;
(*module_info)->num_cpp_defines = hdr->num_cpp_defines;
(*module_info)->num_cpp_includes = hdr->num_cpp_includes;
(*module_info)->num_cl_args = hdr->num_cl_args;
/* The first part of the sp_module_info profile is the auxilary modules.
We skip this section here because it will not be used in the
module_infos. */
profile += hdr->num_aux_modules;
for (i = 0; i < sp_module_num_strings (hdr); i++)
(*module_info)->string_array[current_offset++]
= &(prog_unit->str_table[(profile++)->string_offset]);
/* TODO: SampleFDO also need to add the support to check if the module contains
ASM_STMT. */
}
/* Routine that takes the module id ID as input. It traverse the module header
table HDR from START to END, and returns the relative position of the
profile in the feedback data file. */
static int
sp_find_module_by_id (struct module_sample_hdr *hdr,
int start, int end, unsigned int id)
{
int i;
for (i = start; i < end; i++)
{
if (hdr[i].module_name_index == id)
return i;
}
return -1;
}
/* Read in the module grouping information from the sample profile INFILE.
Use PROG_UNIT to get the string table. */
static void
sp_read_modules (FILE *infile, struct profile *prog_unit)
{
struct fb_sample_hdr *fb_hdr = &(prog_unit->fb_hdr);
unsigned long long module_start = fb_hdr->fb_module_hdr_offset;
unsigned int num_module_hdr = fb_hdr->fb_num_module_hdr;
unsigned int i;
const char *curr_file_name;
void *buf = xmalloc (fb_hdr->fb_module_profile_size);
struct module_sample_hdr *hdr = (struct module_sample_hdr *) buf;
if (fseek (infile, module_start, SEEK_SET) != 0)
error ("read_inline_function(): fseek profile_data error.");
if (fread (buf, 1, fb_hdr->fb_module_profile_size, infile) !=
fb_hdr->fb_module_profile_size)
error ("sp_read_modules(): fread module_hdr error.");
curr_file_name = in_fnames[0];
if (dump_file)
fprintf (dump_file, "\nDoing Module grouping for %s.\n", curr_file_name);
/* Read in the aux module info for the primary module. */
for (i = 0; i < num_module_hdr; i++)
{
char *file_name = &(prog_unit->str_table[hdr[i].module_name_index]);
/* Traverse the modules to find the primarial module. */
if (!strcmp (realname (file_name), realname (curr_file_name)))
{
unsigned int j;
int curr_module = 1, idx = 0;
struct fb_info_module *modules =
(struct fb_info_module *) (((char *)buf) +
hdr[i].module_profile_offset);
module_infos = XCNEWVEC (struct gcov_module_info *,
(hdr[i].num_aux_modules + 1));
sp_add_module (module_infos, modules, &hdr[i], true, prog_unit);
primary_module_id = hdr[i].module_name_index;
if (dump_file)
fprintf (dump_file, "Aux Modules:\n");
for (j = 0; j < hdr[i].num_aux_modules; j++, idx++)
{
struct fb_info_module *aux_modules;
char *aux_name =
&(prog_unit->str_table[modules[j].string_offset]);
if (primary_module_id == modules[j].string_offset)
continue;
idx = sp_find_module_by_id (hdr, idx, num_module_hdr,
modules[j].string_offset);
if (idx < 0)
error ("sp_read_modules(): aux module %s doesn't exist.",
aux_name);
aux_modules =
(struct fb_info_module *) (((char *)buf) +
hdr[idx].module_profile_offset);
sp_add_module (&module_infos[curr_module++], aux_modules,
&hdr[idx], false, prog_unit);
add_input_filename (aux_name);
if (dump_file)
fprintf (dump_file, "\t%s\n", aux_name);
}
/* Because we already found the primary module, and finished
processing, we can stop traversing other modules. */
break;
}
}
}
/* Read sample profile file with filename IN_FILENAME to initialize sp_htab and
PROG_UNIT. Return the number of <> tuples. */
static unsigned long long
sp_reader (const char *in_filename, struct profile *prog_unit)
{
unsigned int num_funcs;
FILE *in_file;
unsigned int i;
unsigned long long j;
unsigned long long num_lines;
unsigned long long profile_offset;
unsigned long long num_samples = 0;
if ((in_file = fopen (in_filename, "r")) == NULL)
{
error ("Error opening sample profile file %s.\n", in_filename);
return 0;
}
if (read_file_header (in_file, prog_unit) != 0)
{
error ("Error reading file header of %s.\n", in_filename);
fclose (in_file);
return 0;
}
if (read_string_table (in_file, prog_unit) != 0)
{
error ("Error reading string table of %s.\n", in_filename);
if (prog_unit->str_table)
free (prog_unit->str_table);
fclose (in_file);
return 0;
}
if (flag_dyn_ipa)
sp_read_modules (in_file, prog_unit);
num_funcs = prog_unit->fb_hdr.fb_func_hdr_num;
for (i = 0; i < num_funcs; ++i)
{
struct func_sample_hdr func_hdr;
struct sample_freq_detail **slot;
if (read_function_header (in_file, i, prog_unit, &func_hdr) != 0)
{
error ("Error reading the %dth function header of %s.\n", i,
in_filename);
if (prog_unit->str_table)
free (prog_unit->str_table);
fclose (in_file);
return 0;
}
num_lines = func_hdr.func_num_freq_entries;
sample_buf = (struct sample_freq_detail *)
xcalloc (num_lines + 1, sizeof (struct sample_freq_detail));
sample_buf[0].func_name =
&(prog_unit->str_table[func_hdr.func_name_index]);
sample_buf[0].filename =
&(prog_unit->str_table[func_hdr.func_name_index]);
sample_buf[0].line_num = 0;
sample_buf[0].discriminator = 0;
sample_buf[0].freq = func_hdr.entry_count;
sample_buf[0].num_instr = 0;
slot = (struct sample_freq_detail **)
htab_find_slot (sp_htab, &sample_buf[0], INSERT);
if (!*slot) {
*slot = &sample_buf[0];
}
for (j = 1; j <= num_lines; ++j)
{
struct fb_info_freq sample;
/* Get the offset of the frequency profile. */
profile_offset = prog_unit->fb_hdr.fb_profile_offset +
func_hdr.func_profile_offset +
func_hdr.func_freq_offset + /* This should be 0 */
(j - 1) * sizeof (sample);
if (fseek (in_file, profile_offset, SEEK_SET) != 0)
return 0;
if (fread (&sample, 1, sizeof (sample), in_file) != sizeof (sample))
return 0;
sample_buf[j].func_name =
&(prog_unit->str_table[func_hdr.func_name_index]);
sample_buf[j].filename =
&(prog_unit->str_table[sample.filename_offset]);
sample_buf[j].line_num = sample.line_num;
sample_buf[j].discriminator = DISCRIM (sample.discriminator);
sample_buf[j].freq = sample.freq;
sample_buf[j].num_instr = sample.num_instr;
if (sample.freq > sp_max_count)
sp_max_count = sample.freq;
sample_buf[j].num_value = sample.num_value;
if (sample.num_value > 0) {
unsigned long long k;
struct fb_info_hist *hist_buf = (struct fb_info_hist *)
alloca (sample.num_value * sizeof (struct fb_info_hist));
/* Get the offset of the value profile. */
profile_offset = prog_unit->fb_hdr.fb_profile_offset
+ func_hdr.func_hist_table_offset
+ sample.hist_offset * sizeof (struct fb_info_hist);
if (fseek (in_file, profile_offset, SEEK_SET) != 0)
return 0;
if (fread (hist_buf,
sample.num_value,
sizeof (struct fb_info_hist),
in_file) != sizeof (struct fb_info_hist)) {
return 0;
}
sample_buf[j].values = (struct sample_hist *)
xcalloc (sample.num_value, sizeof (struct sample_hist));
for (k = 0; k < sample.num_value; k++) {
sample_buf[j].values[k].type = hist_buf[k].type;
if (hist_buf[k].type == CALL_HIST)
{
sample_buf[j].values[k].value.func_name =
&(prog_unit->str_table[hist_buf[k].value]);
sp_add_indirect_call (
&(prog_unit->str_table[func_hdr.func_name_index]),
&(prog_unit->str_table[hist_buf[k].value]),
hist_buf[k].count);
}
else
{
sample_buf[j].values[k].value.value = hist_buf[k].value;
}
sample_buf[j].values[k].count = hist_buf[k].count;
}
}
/* Insert new sample into hash table. */
slot = (struct sample_freq_detail **)
htab_find_slot (sp_htab, &sample_buf[j], INSERT);
if (*slot)
{
if (PARAM_VALUE (PARAM_SAMPLEFDO_USE_DISCRIMINATORS))
{
char *func_name =
&(prog_unit->str_table[func_hdr.func_name_index]);
inform (0, "Duplicate entry: %s:%d func_name:%s",
sample_buf[j].filename,
sample_buf[j].line_num, func_name);
}
else
{
/* When not using discriminators, merge multiple
entries with different discriminator values */
(*slot)->freq += sample_buf[j].freq;
(*slot)->num_instr += sample_buf[j].num_instr;
}
}
else
{
*slot = &sample_buf[j];
num_samples++;
}
}
if (func_hdr.func_num_inline_entries > 0)
num_samples = read_inline_function (in_file, prog_unit, &func_hdr,
num_samples);
}
fclose (in_file);
return num_samples;
}
static int
compare (const void *a, const void *b) {
const struct sample_hist *x = (const struct sample_hist *) a;
const struct sample_hist *y = (const struct sample_hist *) b;
if (x->type != y->type)
{
return y->type - x->type;
}
else if (x->count != y->count)
{
return y->count - x->count;
}
else
{
if (x->type == CALL_HIST)
return strcmp (x->value.func_name, y->value.func_name);
else
return y->value.value - x->value.value;
}
}
/* Compute the BB execution count from the sample profile data. */
void
sp_annotate_bb (basic_block bb)
{
gimple_stmt_iterator si;
/* The number of IRs in a BB. */
int num_ir = 0, num_instr_sampled = 0;
gcov_type sum_ir_count = 0;
gcov_type bb_max_count = 0;
int lineno, discriminator;
struct expanded_inline_location inline_stack[FB_INLINE_MAX_STACK];
int inline_stack_depth;
int num_lines = 0, num_inlines = 0;
void *lines[MAX_LINES_PER_BASIC_BLOCK];
void *lines_inline[MAX_LINES_PER_BASIC_BLOCK];
if (flag_sample_profile_use_entry && bb == ENTRY_BLOCK_PTR)
{
struct sample_freq_detail ir_loc;
struct sample_freq_detail **slot;
struct sample_freq_detail *htab_entry = NULL;
ir_loc.filename
= sp_get_real_funcname (current_function_assembler_name ());
ir_loc.func_name
= sp_get_real_funcname (current_function_assembler_name ());
ir_loc.line_num = 0;
ir_loc.discriminator = 0;
slot = (struct sample_freq_detail **)
htab_find_slot (sp_htab, &ir_loc, INSERT);
if (*slot) {
htab_entry = *slot;
}
if (htab_entry) {
bb->count = htab_entry->freq;
bb->confidence = HIGH_CONFIDENCE;
if (dump_file)
fprintf (dump_file,
"BB%d: average_count=" HOST_WIDEST_INT_PRINT_DEC ", ",
bb->index, bb->count);
return;
}
}
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
{
hashval_t hash_val;
int i;
gimple stmt = gsi_stmt (si);
lineno = get_lineno (stmt);
if (lineno == -1)
continue;
discriminator = DISCRIM (get_discriminator (stmt));
inline_stack_depth = sp_get_inline_stack (stmt, inline_stack);
gcc_assert (inline_stack_depth < FB_INLINE_MAX_STACK);
if (inline_stack_depth > 0)
{
/* Look in inline hash table for matching entry. */
struct sample_inline_freq inline_loc;
struct sample_inline_freq *inline_htab_entry;
inline_loc.depth = inline_stack_depth;
inline_loc.inline_stack = inline_stack;
inline_loc.func_name
= sp_get_real_funcname (current_function_assembler_name ());
inline_loc.filename = gimple_filename (stmt);
inline_loc.line_num = lineno;
inline_loc.discriminator = discriminator;
inline_htab_entry = (struct sample_inline_freq *)
htab_find (sp_inline_htab, (void *) &inline_loc);
if (!inline_htab_entry)
continue;
num_ir++;
if (flag_value_profile_transformations
&& inline_htab_entry->num_value > 0)
{
qsort (inline_htab_entry->values,
inline_htab_entry->num_value,
sizeof(struct sample_hist),
compare);
gimple_sample_vpt (stmt,
inline_htab_entry->values,
inline_htab_entry->num_value);
}
for (i = num_inlines - 1; i >= 0; i--)
if ((void *) inline_htab_entry == lines_inline[i])
break;
if (i >= 0)
continue;
gcc_assert (num_inlines < MAX_LINES_PER_BASIC_BLOCK);
lines_inline[num_inlines++] = (void *) inline_htab_entry;
sum_ir_count += inline_htab_entry->freq;
num_instr_sampled += inline_htab_entry->num_instr;
if (bb_max_count < inline_htab_entry->freq)
bb_max_count = inline_htab_entry->freq;
if (dump_file)
fprintf (dump_file,
"BB%d: %s line_%d (" HOST_WIDEST_INT_PRINT_DEC ")\n",
bb->index, inline_loc.filename, lineno,
(HOST_WIDEST_INT) inline_htab_entry->freq);
}
else
{
/* Not an inlined location. Look in regular hash table. */
struct sample_freq_detail ir_loc;
struct sample_freq_detail *htab_entry;
ir_loc.filename = gimple_filename (stmt);
ir_loc.func_name
= sp_get_real_funcname (current_function_assembler_name ());
ir_loc.line_num = lineno;
ir_loc.discriminator = discriminator;
hash_val = create_hash_string (ir_loc.filename, lineno,
discriminator);
htab_entry = (struct sample_freq_detail *)
htab_find_with_hash (sp_htab, (void *) &ir_loc, hash_val);
if (!htab_entry)
continue;
num_ir++;
if (flag_value_profile_transformations && htab_entry->num_value > 0)
{
qsort (htab_entry->values,
htab_entry->num_value,
sizeof(struct sample_hist),
compare);
gimple_sample_vpt (stmt,
htab_entry->values,
htab_entry->num_value);
}
for (i = num_lines - 1; i >= 0; i--)
if ((void *) htab_entry == lines[i])
break;
if (i >= 0)
continue;
gcc_assert (num_lines < MAX_LINES_PER_BASIC_BLOCK);
lines[num_lines++] = (void *) htab_entry;
sum_ir_count += htab_entry->freq;
num_instr_sampled += htab_entry->num_instr;
if (bb_max_count < htab_entry->freq)
bb_max_count = htab_entry->freq;
if (dump_file)
fprintf (dump_file,
"BB%d: %s line_%d (" HOST_WIDEST_INT_PRINT_DEC ")\n",
bb->index, ir_loc.filename, lineno,
(HOST_WIDEST_INT) htab_entry->freq);
}
}
if (num_instr_sampled > 0)
{
if (flag_sample_profile_aggregate_using
== SAMPLE_PROFILE_AGGREGATE_USING_MAX)
bb->count = bb_max_count;
else if (flag_sample_profile_aggregate_using
== SAMPLE_PROFILE_AGGREGATE_USING_AVG)
bb->count = sum_ir_count / num_instr_sampled;
else
gcc_unreachable();
/* If there are many statements in a BB, but no instructions
were sampled, one can be confident in the sampled profile
count. */
if (bb->count == 0
&& num_ir > PARAM_VALUE (PARAM_SAMPLEFDO_LARGE_BLOCK_THRESH)
&& num_instr_sampled > PARAM_VALUE (PARAM_SAMPLEFDO_INST_THRESH))
bb->confidence = HIGH_CONFIDENCE;
else
bb->confidence = NORMAL_CONFIDENCE;
}
else
{
bb->count = 0;
bb->confidence = LOW_CONFIDENCE;
}
if (dump_file)
{
fprintf (dump_file,
"BB%d: average_count=" HOST_WIDEST_INT_PRINT_DEC ", ",
bb->index, bb->count);
fprintf (dump_file, "maximal_count=" HOST_WIDEST_INT_PRINT_DEC ". ",
bb_max_count);
fprintf (dump_file, "num_ir=%u, num_instr_sampled=%u.\n", num_ir,
num_instr_sampled);
}
}
/* Initialize edge counts and edge probabilities (e->count,
e->probability) with sample count data. */
static void
sp_init_cfg (void)
{
basic_block bb;
edge e;
edge_iterator ei;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
{
gcov_type total_count = 0;
int num_edge = 0;
FOR_EACH_EDGE (e, ei, bb->succs)
{
total_count += e->dest->count;
num_edge++;
}
if (total_count == 0)
{
/* If none of the successor blocks have samples, divide
the source block's weight evenly among the out
edges. */
FOR_EACH_EDGE (e, ei, bb->succs)
{
e->count = e->src->count / num_edge;
e->probability = REG_BR_PROB_BASE / num_edge;
}
}
else
{
/* Compute edge probabilities using the source and dest
basic block counts. This computation is wrong for
critical edges, but we rely on MCF to clean up these
inaccuracies. */
FOR_EACH_EDGE (e, ei, bb->succs)
{
e->count = e->src->count * e->dest->count / total_count;
e->probability = REG_BR_PROB_BASE * e->dest->count / total_count;
}
}
}
/* Initialize ENTRY and EXIT counts. */
if (!flag_sample_profile_use_entry)
{
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
ENTRY_BLOCK_PTR->count += e->count;
}
}
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
{
EXIT_BLOCK_PTR->count += e->count;
}
}
/* Adjust the BB and edge frequency. */
void
sp_smooth_cfg (void)
{
compact_blocks ();
sp_init_cfg ();
add_noreturn_fake_exit_edges ();
mcf_smooth_cfg ();
remove_fake_exit_edges ();
counts_to_freqs ();
}
/* Set the global variables to mark that the profile is availabe for the
function. Also set the the sum_max to MAX_COUNT. */
void
sp_set_sample_profile (gcov_type max_count)
{
if (!profile_info || profile_status != PROFILE_READ)
{
profile_status = PROFILE_READ;
sp_profile_info->runs = 1;
sp_profile_info->sum_max = 0;
profile_info = sp_profile_info;
}
if (max_count > profile_info->sum_max)
sp_profile_info->sum_max = max_count;
}
/* Annotate CFG with sample profile. Sets basic block and edge counts and
profile_info using sample profile input. The basic block and edge counts
are "smoothed" to be flow-consistent using a Minimum-Cost Flow algorithm. */
static void
sp_annotate_cfg (void)
{
basic_block bb;
int num_bb_annotated = 0;
gcov_type func_max_count = 0;
cgraph_need_artificial_indirect_call_edges = 0;
if (dump_file)
{
fprintf (dump_file,
"\nAnnotate CFG for function %s() in file %s with sample profile.\n",
lang_hooks.decl_printable_name (current_function_decl, 2),
main_input_filename);
fprintf (dump_file, "n_basic_blocks=%d, n_edges=%d.\n\n",
n_basic_blocks, n_edges);
fprintf (dump_file, "\nStatistics for sp_htab:\n");
print_hash_table_statistics (sp_htab);
fprintf (dump_file, "\nStatistics for sp_inline_htab:\n");
print_hash_table_statistics (sp_inline_htab);
}
/* Annotate basic blocks with sample data. */
FOR_ALL_BB (bb)
{
sp_annotate_bb (bb);
if (bb->count)
{
num_bb_annotated++;
if (bb->count > func_max_count)
func_max_count = bb->count;
}
}
if (dump_file)
{
fprintf (dump_file, "\n%d of %d BBs are sampled. ", num_bb_annotated,
n_basic_blocks - 2);
fprintf (dump_file, "func_max_count=" HOST_WIDEST_INT_PRINT_DEC ", ",
func_max_count);
fprintf (dump_file, "sp_max_count=" HOST_WIDEST_INT_PRINT_DEC ".\n",
sp_max_count);
}
if (num_bb_annotated > 1
|| (num_bb_annotated == 1 && (n_basic_blocks < MIN_SAMPLE_BB_COUNT)))
{
sp_smooth_cfg ();
sp_set_sample_profile (sp_max_count);
if (flag_value_profile_transformations)
{
gimple_register_value_prof_hooks();
value_profile_transformations ();
}
}
else
{
FOR_EACH_BB (bb)
bb->count = 0;
}
}
/* Read sample file to initialize sp_htab. Called in toplev.c.
This function works in the file-level instead of function-level.
This can save time. */
void
init_sample_profile (void)
{
if (flag_branch_probabilities)
{
inform
(0, "Cannot set both -fbranch-probabilities and -fsample-profile. "
"Disable -fsample-profile now.");
flag_sample_profile = 0;
return;
}
if (sample_data_name == NULL)
sample_data_name = DEFAULT_SAMPLE_DATAFILE;
sp_htab = htab_create_alloc ((size_t) SP_HTAB_INIT_SIZE,
sp_info_hash,
sp_info_eq,
sp_info_del,
xcalloc,
free);
sp_inline_htab = htab_create_alloc ((size_t) SP_INLINE_HTAB_INIT_SIZE,
sp_inline_info_hash,
sp_inline_info_eq,
sp_inline_info_del,
xcalloc,
free);
if (flag_dyn_ipa)
sp_funcname_htab = htab_create_alloc ((size_t) SP_FUNCNAME_HTAB_INIT_SIZE,
sp_funcname_hash,
sp_funcname_eq,
0,
xcalloc,
free);
sp_indirect_htab = htab_create_alloc ((size_t) SP_FUNCNAME_HTAB_INIT_SIZE,
sp_indirect_hash,
sp_indirect_eq,
0,
xcalloc,
free);
sp_num_samples = sp_reader (sample_data_name, &prog_unit);
sp_profile_info =
(struct gcov_ctr_summary *) xcalloc (1, sizeof (struct gcov_ctr_summary));
if (!sp_num_samples)
{
inform
(0, "No available data in the sample file %s. "
"Disable -fsample-profile now.",
sample_data_name);
flag_sample_profile = 0;
}
else
inform (0, "There are %llu samples in file %s", sp_num_samples,
sample_data_name);
}
/* Finalize some data structures. Called in toplev.c. */
void
end_sample_profile (void)
{
if (prog_unit.str_table)
free (prog_unit.str_table);
if (sp_htab)
htab_delete (sp_htab);
sp_htab = NULL;
free (sample_buf);
if (sp_inline_htab)
htab_delete (sp_inline_htab);
sp_inline_htab = NULL;
if (sp_indirect_htab)
htab_delete (sp_indirect_htab);
free (inline_sample_buf);
if (sp_profile_info)
free (sp_profile_info);
}
/* Main entry of sample_profile pass. */
static unsigned int
execute_sample_profile (void)
{
/* Annotate CFG with sample profile. */
flow_call_edges_add (NULL);
compact_blocks ();
remove_fake_edges ();
sp_annotate_cfg ();
cfun->after_tree_profile = 1;
return 0;
}
static bool
gate_sample_profile (void)
{
/* This is a redundant check. Just for safety. */
gcc_assert (!(flag_sample_profile && flag_branch_probabilities));
return flag_sample_profile;
}
struct gimple_opt_pass pass_tree_sample_profile = {
{
GIMPLE_PASS,
"sample_profile",
gate_sample_profile,
execute_sample_profile,
NULL,
NULL,
0,
TV_TREE_SAMPLE,
PROP_cfg,
0,
0,
0,
TODO_dump_func,
}
};
/* Entry for profile_dump pass. */
static unsigned int
execute_profile_dump (void)
{
char *dump_cfg_filename = NULL;
if (flag_branch_probabilities)
{
/* Dump edge profile. */
dump_cfg_filename = concat(dump_base_name, ".prof.compare.branch", NULL);
}
else if (flag_sample_profile)
{
/* Dump sample profile. */
dump_cfg_filename = concat(dump_base_name, ".prof.compare.sample", NULL);
}
if (dump_cfg_filename)
{
dump_cfg_profile (dump_cfg_filename);
free (dump_cfg_filename);
}
return 0;
}
static bool
gate_profile_dump (void)
{
return (flag_profile_dump
&& (flag_sample_profile || flag_branch_probabilities));
}
struct gimple_opt_pass pass_tree_profile_dump = {
{
GIMPLE_PASS,
"profile_dump",
gate_profile_dump,
execute_profile_dump,
NULL,
NULL,
0,
0,
PROP_cfg,
0,
0,
0,
0
}
};