| /* Optimization statistics functions. | |
| Copyright (C) 2008-2026 Free Software Foundation, Inc. | |
| Contributed by Richard Guenther <rguenther@suse.de> | |
| 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 | |
| <http://www.gnu.org/licenses/>. */ | |
| #include "config.h" | |
| #include "system.h" | |
| #include "coretypes.h" | |
| #include "function.h" | |
| #include "tree-pass.h" | |
| #include "context.h" | |
| #include "pass_manager.h" | |
| #include "tree.h" | |
| static int statistics_dump_nr; | |
| static dump_flags_t statistics_dump_flags; | |
| static FILE *statistics_dump_file; | |
| /* Statistics entry. A integer counter associated to a string ID | |
| and value. */ | |
| struct statistics_counter { | |
| const char *id; | |
| int val; | |
| bool histogram_p; | |
| unsigned HOST_WIDE_INT count; | |
| unsigned HOST_WIDE_INT prev_dumped_count; | |
| }; | |
| /* Hashtable helpers. */ | |
| struct stats_counter_hasher : pointer_hash <statistics_counter> | |
| { | |
| static inline hashval_t hash (const statistics_counter *); | |
| static inline bool equal (const statistics_counter *, | |
| const statistics_counter *); | |
| static inline void remove (statistics_counter *); | |
| }; | |
| /* Hash a statistic counter by its string ID. */ | |
| inline hashval_t | |
| stats_counter_hasher::hash (const statistics_counter *c) | |
| { | |
| return htab_hash_string (c->id) + c->val; | |
| } | |
| /* Compare two statistic counters by their string IDs. */ | |
| inline bool | |
| stats_counter_hasher::equal (const statistics_counter *c1, | |
| const statistics_counter *c2) | |
| { | |
| return c1->val == c2->val && strcmp (c1->id, c2->id) == 0; | |
| } | |
| /* Free a statistics entry. */ | |
| inline void | |
| stats_counter_hasher::remove (statistics_counter *v) | |
| { | |
| free (const_cast<char *> (v->id)); | |
| free (v); | |
| } | |
| typedef hash_table<stats_counter_hasher> stats_counter_table_type; | |
| /* Array of statistic hashes, indexed by pass id. */ | |
| static stats_counter_table_type **statistics_hashes; | |
| static unsigned nr_statistics_hashes; | |
| /* Return the current hashtable to be used for recording or printing | |
| statistics. */ | |
| static stats_counter_table_type * | |
| curr_statistics_hash (bool alloc = true) | |
| { | |
| unsigned idx; | |
| gcc_assert (current_pass->static_pass_number >= 0); | |
| idx = current_pass->static_pass_number; | |
| if (idx < nr_statistics_hashes | |
| && statistics_hashes[idx]) | |
| return statistics_hashes[idx]; | |
| if (!alloc) | |
| return nullptr; | |
| if (idx >= nr_statistics_hashes) | |
| { | |
| statistics_hashes = XRESIZEVEC (stats_counter_table_type *, | |
| statistics_hashes, idx+1); | |
| memset (statistics_hashes + nr_statistics_hashes, 0, | |
| (idx + 1 - nr_statistics_hashes) | |
| * sizeof (stats_counter_table_type *)); | |
| nr_statistics_hashes = idx + 1; | |
| } | |
| statistics_hashes[idx] = new stats_counter_table_type (15); | |
| return statistics_hashes[idx]; | |
| } | |
| /* Helper function to return asmname or name of FN | |
| depending on whether asmname option is set. */ | |
| static const char * | |
| get_function_name (struct function *fn) | |
| { | |
| if ((statistics_dump_flags & TDF_ASMNAME) | |
| && fn && DECL_ASSEMBLER_NAME_SET_P (fn->decl)) | |
| { | |
| tree asmname = decl_assembler_name (fn->decl); | |
| if (asmname) | |
| return IDENTIFIER_POINTER (asmname); | |
| } | |
| return function_name (fn); | |
| } | |
| /* Helper for statistics_fini_pass. Print the counter difference | |
| since the last dump for the pass dump files. */ | |
| int | |
| statistics_fini_pass_1 (statistics_counter **slot, | |
| void *data ATTRIBUTE_UNUSED) | |
| { | |
| statistics_counter *counter = *slot; | |
| unsigned HOST_WIDE_INT count = counter->count - counter->prev_dumped_count; | |
| if (count == 0) | |
| return 1; | |
| if (counter->histogram_p) | |
| fprintf (dump_file, "%s == %d: " HOST_WIDE_INT_PRINT_DEC "\n", | |
| counter->id, counter->val, count); | |
| else | |
| fprintf (dump_file, "%s: " HOST_WIDE_INT_PRINT_DEC "\n", | |
| counter->id, count); | |
| counter->prev_dumped_count = counter->count; | |
| return 1; | |
| } | |
| /* Helper for statistics_fini_pass. Print the counter difference | |
| since the last dump for the statistics dump. */ | |
| int | |
| statistics_fini_pass_2 (statistics_counter **slot, | |
| void *data ATTRIBUTE_UNUSED) | |
| { | |
| statistics_counter *counter = *slot; | |
| unsigned HOST_WIDE_INT count = counter->count - counter->prev_dumped_count; | |
| if (count == 0) | |
| return 1; | |
| counter->prev_dumped_count = counter->count; | |
| if (counter->histogram_p) | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s == %d\" \"%s\" " HOST_WIDE_INT_PRINT_DEC "\n", | |
| current_pass->static_pass_number, | |
| current_pass->name, | |
| counter->id, counter->val, | |
| get_function_name (cfun), | |
| count); | |
| else | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s\" \"%s\" " HOST_WIDE_INT_PRINT_DEC "\n", | |
| current_pass->static_pass_number, | |
| current_pass->name, | |
| counter->id, | |
| get_function_name (cfun), | |
| count); | |
| counter->prev_dumped_count = counter->count; | |
| return 1; | |
| } | |
| /* Helper for statistics_fini_pass, reset the counters. */ | |
| int | |
| statistics_fini_pass_3 (statistics_counter **slot, | |
| void *data ATTRIBUTE_UNUSED) | |
| { | |
| statistics_counter *counter = *slot; | |
| counter->prev_dumped_count = counter->count; | |
| return 1; | |
| } | |
| /* Dump the current statistics incrementally. */ | |
| void | |
| statistics_fini_pass (void) | |
| { | |
| if (current_pass->static_pass_number == -1) | |
| return; | |
| stats_counter_table_type *stat_hash = curr_statistics_hash (false); | |
| if (dump_file | |
| && dump_flags & TDF_STATS) | |
| { | |
| fprintf (dump_file, "\n"); | |
| fprintf (dump_file, "Pass statistics of \"%s\": ", current_pass->name); | |
| fprintf (dump_file, "----------------\n"); | |
| if (stat_hash) | |
| stat_hash->traverse_noresize <void *, statistics_fini_pass_1> (NULL); | |
| fprintf (dump_file, "\n"); | |
| } | |
| if (!stat_hash) | |
| return; | |
| if (statistics_dump_file | |
| && !(statistics_dump_flags & TDF_STATS | |
| || statistics_dump_flags & TDF_DETAILS)) | |
| stat_hash->traverse_noresize <void *, statistics_fini_pass_2> (NULL); | |
| stat_hash->traverse_noresize <void *, statistics_fini_pass_3> (NULL); | |
| } | |
| /* Helper for printing summary information. */ | |
| int | |
| statistics_fini_1 (statistics_counter **slot, opt_pass *pass) | |
| { | |
| statistics_counter *counter = *slot; | |
| if (counter->count == 0) | |
| return 1; | |
| if (counter->histogram_p) | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s == %d\" " HOST_WIDE_INT_PRINT_DEC "\n", | |
| pass->static_pass_number, | |
| pass->name, | |
| counter->id, counter->val, | |
| counter->count); | |
| else | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s\" " HOST_WIDE_INT_PRINT_DEC "\n", | |
| pass->static_pass_number, | |
| pass->name, | |
| counter->id, | |
| counter->count); | |
| return 1; | |
| } | |
| /* Finish the statistics and dump summary information. */ | |
| void | |
| statistics_fini (void) | |
| { | |
| gcc::pass_manager *passes = g->get_passes (); | |
| if (!statistics_dump_file) | |
| return; | |
| if (statistics_dump_flags & TDF_STATS) | |
| { | |
| unsigned i; | |
| for (i = 0; i < nr_statistics_hashes; ++i) | |
| if (statistics_hashes[i] | |
| && passes->get_pass_for_id (i) != NULL) | |
| statistics_hashes[i] | |
| ->traverse_noresize <opt_pass *, statistics_fini_1> | |
| (passes->get_pass_for_id (i)); | |
| } | |
| dump_end (statistics_dump_nr, statistics_dump_file); | |
| } | |
| /* Register the statistics dump file. */ | |
| void | |
| statistics_early_init (void) | |
| { | |
| gcc::dump_manager *dumps = g->get_dumps (); | |
| statistics_dump_nr = dumps->dump_register (".statistics", "statistics", | |
| "statistics", DK_tree, | |
| OPTGROUP_NONE, | |
| false); | |
| } | |
| /* Init the statistics. */ | |
| void | |
| statistics_init (void) | |
| { | |
| gcc::dump_manager *dumps = g->get_dumps (); | |
| statistics_dump_file = dump_begin (statistics_dump_nr, NULL); | |
| statistics_dump_flags = dumps->get_dump_file_info (statistics_dump_nr)->pflags; | |
| } | |
| /* Lookup or add a statistics counter in the hashtable HASH with ID, VAL | |
| and HISTOGRAM_P. */ | |
| static statistics_counter * | |
| lookup_or_add_counter (stats_counter_table_type *hash, const char *id, int val, | |
| bool histogram_p) | |
| { | |
| statistics_counter **counter; | |
| statistics_counter c; | |
| c.id = id; | |
| c.val = val; | |
| counter = hash->find_slot (&c, INSERT); | |
| if (!*counter) | |
| { | |
| *counter = XNEW (statistics_counter); | |
| (*counter)->id = xstrdup (id); | |
| (*counter)->val = val; | |
| (*counter)->histogram_p = histogram_p; | |
| (*counter)->prev_dumped_count = 0; | |
| (*counter)->count = 0; | |
| } | |
| return *counter; | |
| } | |
| /* Add statistics information about event ID in function FN. | |
| This will increment the counter associated with ID by INCR. | |
| It will also dump the event to the global statistics file if requested. */ | |
| void | |
| statistics_counter_event (struct function *fn, const char *id, int incr) | |
| { | |
| statistics_counter *counter; | |
| if ((!(dump_flags & TDF_STATS) | |
| && !statistics_dump_file) | |
| || incr == 0) | |
| return; | |
| if (current_pass | |
| && current_pass->static_pass_number != -1) | |
| { | |
| counter = lookup_or_add_counter (curr_statistics_hash (), id, 0, false); | |
| gcc_assert (!counter->histogram_p); | |
| counter->count += incr; | |
| } | |
| if (!statistics_dump_file | |
| || !(statistics_dump_flags & TDF_DETAILS)) | |
| return; | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s\" \"%s\" %d\n", | |
| current_pass ? current_pass->static_pass_number : -1, | |
| current_pass ? current_pass->name : "none", | |
| id, | |
| get_function_name (fn), | |
| incr); | |
| } | |
| /* Add statistics information about event ID in function FN with the | |
| histogram value VAL. | |
| It will dump the event to the global statistics file if requested. */ | |
| void | |
| statistics_histogram_event (struct function *fn, const char *id, int val) | |
| { | |
| statistics_counter *counter; | |
| if (!(dump_flags & TDF_STATS) | |
| && !statistics_dump_file) | |
| return; | |
| counter = lookup_or_add_counter (curr_statistics_hash (), id, val, true); | |
| gcc_assert (counter->histogram_p); | |
| counter->count += 1; | |
| if (!statistics_dump_file | |
| || !(statistics_dump_flags & TDF_DETAILS)) | |
| return; | |
| fprintf (statistics_dump_file, | |
| "%d %s \"%s == %d\" \"%s\" 1\n", | |
| current_pass->static_pass_number, | |
| current_pass->name, | |
| id, val, | |
| get_function_name (fn)); | |
| } |