diff --git a/gcc/coverage.cc b/gcc/coverage.cc index 8ece5db680e614f8225d9e8407dd89bd27020b4d..5a0f69a7d9e5a430b14c87b646251f7742a9202e 100644 --- a/gcc/coverage.cc +++ b/gcc/coverage.cc @@ -292,11 +292,14 @@ read_counts_file (void) gcov_close (); } -/* Returns the counters for a particular tag. */ +/* Returns the counters for a particular tag. When N_COUNTS is a pointer, + the actual number of counters found in the profile data is written back + through it, allowing the caller to know the true buffer size (essential + for TOPN counters whose size is variable). */ gcov_type * get_coverage_counts (unsigned counter, unsigned cfg_checksum, - unsigned lineno_checksum, unsigned int n_counts) + unsigned lineno_checksum, unsigned int *n_counts) { counts_entry *entry, elt; @@ -348,12 +351,12 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum, if (entry->cfg_checksum != cfg_checksum || (counter != GCOV_COUNTER_V_INDIR && counter != GCOV_COUNTER_V_TOPN - && entry->n_counts != n_counts)) + && entry->n_counts != *n_counts)) { static int warned = 0; bool warning_printed = false; - if (entry->n_counts != n_counts) + if (entry->n_counts != *n_counts) warning_printed = warning_at (DECL_SOURCE_LOCATION (current_function_decl), OPT_Wcoverage_mismatch, @@ -361,7 +364,7 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum, "does not match " "its profile data (counter %qs, expected %i and have %i)", current_function_decl, - ctr_names[counter], entry->n_counts, n_counts); + ctr_names[counter], entry->n_counts, *n_counts); else warning_printed = warning_at (DECL_SOURCE_LOCATION (current_function_decl), @@ -377,7 +380,7 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum, "use -Wno-error=coverage-mismatch to tolerate " "the mismatch but performance may drop if the " "function is hot\n"); - + if (!seen_error () && !warned++) { @@ -404,9 +407,26 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum, current_function_decl); } + *n_counts = entry->n_counts; return entry->counts; } +/* Overload: returns the counters for a particular tag, verifying that the + counter count matches the expectation exactly. Used by non-TOPN callers + that pass n_counts by value. */ + +gcov_type * +get_coverage_counts (unsigned counter, unsigned cfg_checksum, + unsigned lineno_checksum, unsigned int n_counts) +{ + unsigned int n_counts2 = n_counts; + gcov_type *ret + = get_coverage_counts (counter, cfg_checksum, + lineno_checksum, &n_counts2); + gcc_assert (!ret || n_counts2 == n_counts); + return ret; +} + /* Allocate NUM counters of type COUNTER. Returns nonzero if the allocation succeeded. */ diff --git a/gcc/coverage.h b/gcc/coverage.h index 0ac046c88d5fdaa65b7f6e91ef304db00b19fbc5..a4a97f99715e843c4bc9c62ca0723a5110dadbb4 100644 --- a/gcc/coverage.h +++ b/gcc/coverage.h @@ -54,6 +54,12 @@ extern gcov_type *get_coverage_counts (unsigned /*counter*/, unsigned /*cfg_checksum*/, unsigned /*lineno_checksum*/, unsigned /*n_counts*/); +/* Overload: returns actual n_counts from profile data through pointer, + essential for TOPN counters with variable-length data (PR 103652). */ +extern gcov_type *get_coverage_counts (unsigned /*counter*/, + unsigned /*cfg_checksum*/, + unsigned /*lineno_checksum*/, + unsigned */*n_counts*/); extern tree get_gcov_type (void); extern bool coverage_node_map_initialized_p (void); diff --git a/gcc/profile.cc b/gcc/profile.cc index 40e105258faa03b773f0e75740e2a48c6467fe04..af686ff2d7133ec167c84f00743ce3d355a7ed3f 100644 --- a/gcc/profile.cc +++ b/gcc/profile.cc @@ -898,7 +898,7 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum, histogram_counts[t] = get_coverage_counts (COUNTER_FOR_HIST_TYPE (t), cfg_checksum, lineno_checksum, - n_histogram_counters[t]); + &n_histogram_counters[t]); if (histogram_counts[t]) any = 1; act_count[t] = histogram_counts[t]; @@ -918,20 +918,47 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum, /* TOP N counter uses variable number of counters. */ if (topn_p) { - unsigned total_size; + gcov_type total_size; + bool ignore = false; if (act_count[t]) - total_size = 2 + 2 * act_count[t][1]; + { + total_size = 2 + 2 * act_count[t][1]; + /* Watch for counter corruption (e.g. stale/mismatched .gcda) + and possible memory overflows. PR 103652. */ + if (total_size < 2 + || total_size > (gcov_type) n_histogram_counters[t]) + { + warning_at (DECL_SOURCE_LOCATION (current_function_decl), + OPT_Wcoverage_mismatch, + "number of counters in profile data for function" + " %qD does not match its profile data" + " (counter %s)", + current_function_decl, + hist->type == HIST_TYPE_TOPN_VALUES + ? "topn" : "indir_call"); + total_size = 2; + ignore = true; + act_count[t] = NULL; + } + } else - total_size = 2; + { + total_size = 2; + ignore = true; + } gimple_add_histogram_value (cfun, stmt, hist); hist->n_counters = total_size; hist->hvalue.counters = XNEWVEC (gcov_type, hist->n_counters); for (j = 0; j < hist->n_counters; j++) - if (act_count[t]) + if (!ignore) hist->hvalue.counters[j] = act_count[t][j]; else hist->hvalue.counters[j] = 0; - act_count[t] += hist->n_counters; + if (!ignore) + { + act_count[t] += hist->n_counters; + n_histogram_counters[t] -= hist->n_counters; + } sort_hist_values (hist); } else diff --git a/gcc/testsuite/gcc.dg/tree-prof/pr103652.c b/gcc/testsuite/gcc.dg/tree-prof/pr103652.c new file mode 100644 index 0000000000000000000000000000000000000000..d5338c728b49279f505145c9768682a32ad59cff --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-prof/pr103652.c @@ -0,0 +1,44 @@ +/* Regression test for PR 103652: ICE in compute_value_histograms when + TOPN indirect-call counter data in .gcda does not match the current + source (more indirect call sites in -fprofile-use than -fprofile-generate). + Without the fix, cc1 segfaults with a buffer overflow. */ +/* { dg-require-profiling "-fprofile-generate" } */ +/* { dg-options "-O2 -Wno-coverage-mismatch" } */ + +typedef int (*fptr)(int); + +__attribute__((noinline)) int op_a(int x) { return x + 1; } +__attribute__((noinline)) int op_b(int x) { return x + 2; } +__attribute__((noinline)) int op_c(int x) { return x + 3; } +__attribute__((noinline)) int op_d(int x) { return x + 4; } + +volatile fptr table[] = { op_a, op_b, op_c, op_d }; + +__attribute__((noinline)) +int work(int n) +{ + int sum = 0; + /* Training run: one indirect call site. */ + for (int i = 0; i < n; i++) + sum += table[i & 3](i); + +#ifdef _PROFILE_USE + /* Feedback run only: additional indirect call sites that were not + present during training. This creates a TOPN histogram counter + mismatch: the .gcda has counters for 1 site, but the compiler + now expects counters for 4 sites. */ + for (int i = 0; i < n; i++) + sum += table[(i + 1) & 3](i); + for (int i = 0; i < n; i++) + sum += table[(i + 2) & 3](i); + for (int i = 0; i < n; i++) + sum += table[(i + 3) & 3](i); +#endif + + return sum; +} + +int main(void) +{ + return !(work(1000) != 0); +}