| /* Dump infrastructure for optimizations and intermediate representation. |
| Copyright (C) 2012-2017 Free Software Foundation, Inc. |
| |
| 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 "options.h" |
| #include "tree.h" |
| #include "gimple-pretty-print.h" |
| #include "diagnostic-core.h" |
| #include "dumpfile.h" |
| #include "context.h" |
| #include "profile-count.h" |
| #include "tree-cfg.h" |
| #include "langhooks.h" |
| |
| /* If non-NULL, return one past-the-end of the matching SUBPART of |
| the WHOLE string. */ |
| #define skip_leading_substring(whole, part) \ |
| (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part)) |
| |
| static dump_flags_t pflags; /* current dump_flags */ |
| static dump_flags_t alt_flags; /* current opt_info flags */ |
| |
| static void dump_loc (dump_flags_t, FILE *, source_location); |
| static FILE *dump_open_alternate_stream (struct dump_file_info *); |
| |
| /* These are currently used for communicating between passes. |
| However, instead of accessing them directly, the passes can use |
| dump_printf () for dumps. */ |
| FILE *dump_file = NULL; |
| FILE *alt_dump_file = NULL; |
| const char *dump_file_name; |
| dump_flags_t dump_flags; |
| |
| #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \ |
| {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, 0, 0, 0, 0, 0, num, \ |
| false, false} |
| |
| /* Table of tree dump switches. This must be consistent with the |
| TREE_DUMP_INDEX enumeration in dumpfile.h. */ |
| static struct dump_file_info dump_files[TDI_end] = |
| { |
| DUMP_FILE_INFO (NULL, NULL, DK_none, 0), |
| DUMP_FILE_INFO (".cgraph", "ipa-cgraph", DK_ipa, 0), |
| DUMP_FILE_INFO (".type-inheritance", "ipa-type-inheritance", DK_ipa, 0), |
| DUMP_FILE_INFO (".ipa-clones", "ipa-clones", DK_ipa, 0), |
| DUMP_FILE_INFO (".original", "tree-original", DK_tree, 0), |
| DUMP_FILE_INFO (".gimple", "tree-gimple", DK_tree, 0), |
| DUMP_FILE_INFO (".nested", "tree-nested", DK_tree, 0), |
| #define FIRST_AUTO_NUMBERED_DUMP 1 |
| #define FIRST_ME_AUTO_NUMBERED_DUMP 3 |
| |
| DUMP_FILE_INFO (NULL, "lang-all", DK_lang, 0), |
| DUMP_FILE_INFO (NULL, "tree-all", DK_tree, 0), |
| DUMP_FILE_INFO (NULL, "rtl-all", DK_rtl, 0), |
| DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0), |
| }; |
| |
| /* Define a name->number mapping for a dump flag value. */ |
| struct dump_option_value_info |
| { |
| const char *const name; /* the name of the value */ |
| const dump_flags_t value; /* the value of the name */ |
| }; |
| |
| /* Table of dump options. This must be consistent with the TDF_* flags |
| in dumpfile.h and opt_info_options below. */ |
| static const struct dump_option_value_info dump_options[] = |
| { |
| {"address", TDF_ADDRESS}, |
| {"asmname", TDF_ASMNAME}, |
| {"slim", TDF_SLIM}, |
| {"raw", TDF_RAW}, |
| {"graph", TDF_GRAPH}, |
| {"details", (TDF_DETAILS | MSG_OPTIMIZED_LOCATIONS |
| | MSG_MISSED_OPTIMIZATION |
| | MSG_NOTE)}, |
| {"cselib", TDF_CSELIB}, |
| {"stats", TDF_STATS}, |
| {"blocks", TDF_BLOCKS}, |
| {"vops", TDF_VOPS}, |
| {"lineno", TDF_LINENO}, |
| {"uid", TDF_UID}, |
| {"stmtaddr", TDF_STMTADDR}, |
| {"memsyms", TDF_MEMSYMS}, |
| {"eh", TDF_EH}, |
| {"alias", TDF_ALIAS}, |
| {"nouid", TDF_NOUID}, |
| {"enumerate_locals", TDF_ENUMERATE_LOCALS}, |
| {"scev", TDF_SCEV}, |
| {"gimple", TDF_GIMPLE}, |
| {"folding", TDF_FOLDING}, |
| {"optimized", MSG_OPTIMIZED_LOCATIONS}, |
| {"missed", MSG_MISSED_OPTIMIZATION}, |
| {"note", MSG_NOTE}, |
| {"optall", MSG_ALL}, |
| {"all", dump_flags_t (~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_GRAPH |
| | TDF_STMTADDR | TDF_RHS_ONLY | TDF_NOUID |
| | TDF_ENUMERATE_LOCALS | TDF_SCEV | TDF_GIMPLE))}, |
| {NULL, 0} |
| }; |
| |
| /* A subset of the dump_options table which is used for -fopt-info |
| types. This must be consistent with the MSG_* flags in dumpfile.h. |
| */ |
| static const struct dump_option_value_info optinfo_verbosity_options[] = |
| { |
| {"optimized", MSG_OPTIMIZED_LOCATIONS}, |
| {"missed", MSG_MISSED_OPTIMIZATION}, |
| {"note", MSG_NOTE}, |
| {"all", MSG_ALL}, |
| {NULL, 0} |
| }; |
| |
| /* Flags used for -fopt-info groups. */ |
| static const struct dump_option_value_info optgroup_options[] = |
| { |
| {"ipa", OPTGROUP_IPA}, |
| {"loop", OPTGROUP_LOOP}, |
| {"inline", OPTGROUP_INLINE}, |
| {"omp", OPTGROUP_OMP}, |
| {"vec", OPTGROUP_VEC}, |
| {"optall", OPTGROUP_ALL}, |
| {NULL, 0} |
| }; |
| |
| gcc::dump_manager::dump_manager (): |
| m_next_dump (FIRST_AUTO_NUMBERED_DUMP), |
| m_extra_dump_files (NULL), |
| m_extra_dump_files_in_use (0), |
| m_extra_dump_files_alloced (0) |
| { |
| } |
| |
| gcc::dump_manager::~dump_manager () |
| { |
| for (size_t i = 0; i < m_extra_dump_files_in_use; i++) |
| { |
| dump_file_info *dfi = &m_extra_dump_files[i]; |
| /* suffix, swtch, glob are statically allocated for the entries |
| in dump_files, and for statistics, but are dynamically allocated |
| for those for passes. */ |
| if (dfi->owns_strings) |
| { |
| XDELETEVEC (const_cast <char *> (dfi->suffix)); |
| XDELETEVEC (const_cast <char *> (dfi->swtch)); |
| XDELETEVEC (const_cast <char *> (dfi->glob)); |
| } |
| /* These, if non-NULL, are always dynamically allocated. */ |
| XDELETEVEC (const_cast <char *> (dfi->pfilename)); |
| XDELETEVEC (const_cast <char *> (dfi->alt_filename)); |
| } |
| XDELETEVEC (m_extra_dump_files); |
| } |
| |
| unsigned int |
| gcc::dump_manager:: |
| dump_register (const char *suffix, const char *swtch, const char *glob, |
| dump_kind dkind, int optgroup_flags, bool take_ownership) |
| { |
| int num = m_next_dump++; |
| |
| size_t count = m_extra_dump_files_in_use++; |
| |
| if (count >= m_extra_dump_files_alloced) |
| { |
| if (m_extra_dump_files_alloced == 0) |
| m_extra_dump_files_alloced = 512; |
| else |
| m_extra_dump_files_alloced *= 2; |
| m_extra_dump_files = XRESIZEVEC (struct dump_file_info, |
| m_extra_dump_files, |
| m_extra_dump_files_alloced); |
| |
| /* Construct a new object in the space allocated above. */ |
| new (m_extra_dump_files + count) dump_file_info (); |
| } |
| else |
| { |
| /* Zero out the already constructed object. */ |
| m_extra_dump_files[count] = dump_file_info (); |
| } |
| |
| m_extra_dump_files[count].suffix = suffix; |
| m_extra_dump_files[count].swtch = swtch; |
| m_extra_dump_files[count].glob = glob; |
| m_extra_dump_files[count].dkind = dkind; |
| m_extra_dump_files[count].optgroup_flags = optgroup_flags; |
| m_extra_dump_files[count].num = num; |
| m_extra_dump_files[count].owns_strings = take_ownership; |
| |
| return count + TDI_end; |
| } |
| |
| |
| /* Allow languages and middle-end to register their dumps before the |
| optimization passes. */ |
| |
| void |
| gcc::dump_manager:: |
| register_dumps () |
| { |
| lang_hooks.register_dumps (this); |
| /* If this assert fails, some FE registered more than |
| FIRST_ME_AUTO_NUMBERED_DUMP - FIRST_AUTO_NUMBERED_DUMP |
| dump files. Bump FIRST_ME_AUTO_NUMBERED_DUMP accordingly. */ |
| gcc_assert (m_next_dump <= FIRST_ME_AUTO_NUMBERED_DUMP); |
| m_next_dump = FIRST_ME_AUTO_NUMBERED_DUMP; |
| dump_files[TDI_original].num = m_next_dump++; |
| dump_files[TDI_gimple].num = m_next_dump++; |
| dump_files[TDI_nested].num = m_next_dump++; |
| } |
| |
| |
| /* Return the dump_file_info for the given phase. */ |
| |
| struct dump_file_info * |
| gcc::dump_manager:: |
| get_dump_file_info (int phase) const |
| { |
| if (phase < TDI_end) |
| return &dump_files[phase]; |
| else if ((size_t) (phase - TDI_end) >= m_extra_dump_files_in_use) |
| return NULL; |
| else |
| return m_extra_dump_files + (phase - TDI_end); |
| } |
| |
| /* Locate the dump_file_info with swtch equal to SWTCH, |
| or return NULL if no such dump_file_info exists. */ |
| |
| struct dump_file_info * |
| gcc::dump_manager:: |
| get_dump_file_info_by_switch (const char *swtch) const |
| { |
| for (unsigned i = 0; i < m_extra_dump_files_in_use; i++) |
| if (0 == strcmp (m_extra_dump_files[i].swtch, swtch)) |
| return &m_extra_dump_files[i]; |
| |
| /* Not found. */ |
| return NULL; |
| } |
| |
| |
| /* Return the name of the dump file for the given phase. |
| The caller is responsible for calling free on the returned |
| buffer. |
| If the dump is not enabled, returns NULL. */ |
| |
| char * |
| gcc::dump_manager:: |
| get_dump_file_name (int phase) const |
| { |
| struct dump_file_info *dfi; |
| |
| if (phase == TDI_none) |
| return NULL; |
| |
| dfi = get_dump_file_info (phase); |
| |
| return get_dump_file_name (dfi); |
| } |
| |
| /* Return the name of the dump file for the given dump_file_info. |
| The caller is responsible for calling free on the returned |
| buffer. |
| If the dump is not enabled, returns NULL. */ |
| |
| char * |
| gcc::dump_manager:: |
| get_dump_file_name (struct dump_file_info *dfi) const |
| { |
| char dump_id[10]; |
| |
| gcc_assert (dfi); |
| |
| if (dfi->pstate == 0) |
| return NULL; |
| |
| /* If available, use the command line dump filename. */ |
| if (dfi->pfilename) |
| return xstrdup (dfi->pfilename); |
| |
| if (dfi->num < 0) |
| dump_id[0] = '\0'; |
| else |
| { |
| /* (null), LANG, TREE, RTL, IPA. */ |
| char suffix = " ltri"[dfi->dkind]; |
| |
| if (snprintf (dump_id, sizeof (dump_id), ".%03d%c", dfi->num, suffix) < 0) |
| dump_id[0] = '\0'; |
| } |
| |
| return concat (dump_base_name, dump_id, dfi->suffix, NULL); |
| } |
| |
| /* For a given DFI, open an alternate dump filename (which could also |
| be a standard stream such as stdout/stderr). If the alternate dump |
| file cannot be opened, return NULL. */ |
| |
| static FILE * |
| dump_open_alternate_stream (struct dump_file_info *dfi) |
| { |
| FILE *stream ; |
| if (!dfi->alt_filename) |
| return NULL; |
| |
| if (dfi->alt_stream) |
| return dfi->alt_stream; |
| |
| stream = strcmp ("stderr", dfi->alt_filename) == 0 |
| ? stderr |
| : strcmp ("stdout", dfi->alt_filename) == 0 |
| ? stdout |
| : fopen (dfi->alt_filename, dfi->alt_state < 0 ? "w" : "a"); |
| |
| if (!stream) |
| error ("could not open dump file %qs: %m", dfi->alt_filename); |
| else |
| dfi->alt_state = 1; |
| |
| return stream; |
| } |
| |
| /* Print source location on DFILE if enabled. */ |
| |
| void |
| dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc) |
| { |
| if (dump_kind) |
| { |
| if (LOCATION_LOCUS (loc) > BUILTINS_LOCATION) |
| fprintf (dfile, "%s:%d:%d: note: ", LOCATION_FILE (loc), |
| LOCATION_LINE (loc), LOCATION_COLUMN (loc)); |
| else if (current_function_decl) |
| fprintf (dfile, "%s:%d:%d: note: ", |
| DECL_SOURCE_FILE (current_function_decl), |
| DECL_SOURCE_LINE (current_function_decl), |
| DECL_SOURCE_COLUMN (current_function_decl)); |
| } |
| } |
| |
| /* Dump gimple statement GS with SPC indentation spaces and |
| EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled. */ |
| |
| void |
| dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, |
| gimple *gs, int spc) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags); |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); |
| } |
| |
| /* Similar to dump_gimple_stmt, except additionally print source location. */ |
| |
| void |
| dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc, |
| dump_flags_t extra_dump_flags, gimple *gs, int spc) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| { |
| dump_loc (dump_kind, dump_file, loc); |
| print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags); |
| } |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| { |
| dump_loc (dump_kind, alt_dump_file, loc); |
| print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); |
| } |
| } |
| |
| /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if |
| DUMP_KIND is enabled. */ |
| |
| void |
| dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, |
| tree t) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| print_generic_expr (dump_file, t, dump_flags | extra_dump_flags); |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags); |
| } |
| |
| |
| /* Similar to dump_generic_expr, except additionally print the source |
| location. */ |
| |
| void |
| dump_generic_expr_loc (int dump_kind, source_location loc, |
| dump_flags_t extra_dump_flags, tree t) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| { |
| dump_loc (dump_kind, dump_file, loc); |
| print_generic_expr (dump_file, t, dump_flags | extra_dump_flags); |
| } |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| { |
| dump_loc (dump_kind, alt_dump_file, loc); |
| print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags); |
| } |
| } |
| |
| /* Output a formatted message using FORMAT on appropriate dump streams. */ |
| |
| void |
| dump_printf (dump_flags_t dump_kind, const char *format, ...) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| { |
| va_list ap; |
| va_start (ap, format); |
| vfprintf (dump_file, format, ap); |
| va_end (ap); |
| } |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| { |
| va_list ap; |
| va_start (ap, format); |
| vfprintf (alt_dump_file, format, ap); |
| va_end (ap); |
| } |
| } |
| |
| /* Similar to dump_printf, except source location is also printed. */ |
| |
| void |
| dump_printf_loc (dump_flags_t dump_kind, source_location loc, |
| const char *format, ...) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| { |
| va_list ap; |
| dump_loc (dump_kind, dump_file, loc); |
| va_start (ap, format); |
| vfprintf (dump_file, format, ap); |
| va_end (ap); |
| } |
| |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| { |
| va_list ap; |
| dump_loc (dump_kind, alt_dump_file, loc); |
| va_start (ap, format); |
| vfprintf (alt_dump_file, format, ap); |
| va_end (ap); |
| } |
| } |
| |
| /* Start a dump for PHASE. Store user-supplied dump flags in |
| *FLAG_PTR. Return the number of streams opened. Set globals |
| DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and |
| set dump_flags appropriately for both pass dump stream and |
| -fopt-info stream. */ |
| |
| int |
| gcc::dump_manager:: |
| dump_start (int phase, dump_flags_t *flag_ptr) |
| { |
| int count = 0; |
| char *name; |
| struct dump_file_info *dfi; |
| FILE *stream; |
| if (phase == TDI_none || !dump_phase_enabled_p (phase)) |
| return 0; |
| |
| dfi = get_dump_file_info (phase); |
| name = get_dump_file_name (phase); |
| if (name) |
| { |
| stream = strcmp ("stderr", name) == 0 |
| ? stderr |
| : strcmp ("stdout", name) == 0 |
| ? stdout |
| : fopen (name, dfi->pstate < 0 ? "w" : "a"); |
| if (!stream) |
| error ("could not open dump file %qs: %m", name); |
| else |
| { |
| dfi->pstate = 1; |
| count++; |
| } |
| free (name); |
| dfi->pstream = stream; |
| dump_file = dfi->pstream; |
| /* Initialize current dump flags. */ |
| pflags = dfi->pflags; |
| } |
| |
| stream = dump_open_alternate_stream (dfi); |
| if (stream) |
| { |
| dfi->alt_stream = stream; |
| count++; |
| alt_dump_file = dfi->alt_stream; |
| /* Initialize current -fopt-info flags. */ |
| alt_flags = dfi->alt_flags; |
| } |
| |
| if (flag_ptr) |
| *flag_ptr = dfi->pflags; |
| |
| return count; |
| } |
| |
| /* Finish a tree dump for PHASE and close associated dump streams. Also |
| reset the globals DUMP_FILE, ALT_DUMP_FILE, and DUMP_FLAGS. */ |
| |
| void |
| gcc::dump_manager:: |
| dump_finish (int phase) |
| { |
| struct dump_file_info *dfi; |
| |
| if (phase < 0) |
| return; |
| dfi = get_dump_file_info (phase); |
| if (dfi->pstream && (!dfi->pfilename |
| || (strcmp ("stderr", dfi->pfilename) != 0 |
| && strcmp ("stdout", dfi->pfilename) != 0))) |
| fclose (dfi->pstream); |
| |
| if (dfi->alt_stream && strcmp ("stderr", dfi->alt_filename) != 0 |
| && strcmp ("stdout", dfi->alt_filename) != 0) |
| fclose (dfi->alt_stream); |
| |
| dfi->alt_stream = NULL; |
| dfi->pstream = NULL; |
| dump_file = NULL; |
| alt_dump_file = NULL; |
| dump_flags = TDI_none; |
| alt_flags = 0; |
| pflags = 0; |
| } |
| |
| /* Begin a tree dump for PHASE. Stores any user supplied flag in |
| *FLAG_PTR and returns a stream to write to. If the dump is not |
| enabled, returns NULL. |
| Multiple calls will reopen and append to the dump file. */ |
| |
| FILE * |
| dump_begin (int phase, dump_flags_t *flag_ptr) |
| { |
| return g->get_dumps ()->dump_begin (phase, flag_ptr); |
| } |
| |
| FILE * |
| gcc::dump_manager:: |
| dump_begin (int phase, dump_flags_t *flag_ptr) |
| { |
| char *name; |
| struct dump_file_info *dfi; |
| FILE *stream; |
| |
| if (phase == TDI_none || !dump_phase_enabled_p (phase)) |
| return NULL; |
| |
| name = get_dump_file_name (phase); |
| if (!name) |
| return NULL; |
| dfi = get_dump_file_info (phase); |
| |
| stream = strcmp ("stderr", name) == 0 |
| ? stderr |
| : strcmp ("stdout", name) == 0 |
| ? stdout |
| : fopen (name, dfi->pstate < 0 ? "w" : "a"); |
| |
| if (!stream) |
| error ("could not open dump file %qs: %m", name); |
| else |
| dfi->pstate = 1; |
| free (name); |
| |
| if (flag_ptr) |
| *flag_ptr = dfi->pflags; |
| |
| /* Initialize current flags */ |
| pflags = dfi->pflags; |
| return stream; |
| } |
| |
| /* Returns nonzero if dump PHASE is enabled for at least one stream. |
| If PHASE is TDI_tree_all, return nonzero if any dump is enabled for |
| any phase. */ |
| |
| int |
| gcc::dump_manager:: |
| dump_phase_enabled_p (int phase) const |
| { |
| if (phase == TDI_tree_all) |
| { |
| size_t i; |
| for (i = TDI_none + 1; i < (size_t) TDI_end; i++) |
| if (dump_files[i].pstate || dump_files[i].alt_state) |
| return 1; |
| for (i = 0; i < m_extra_dump_files_in_use; i++) |
| if (m_extra_dump_files[i].pstate || m_extra_dump_files[i].alt_state) |
| return 1; |
| return 0; |
| } |
| else |
| { |
| struct dump_file_info *dfi = get_dump_file_info (phase); |
| return dfi->pstate || dfi->alt_state; |
| } |
| } |
| |
| /* Returns nonzero if tree dump PHASE has been initialized. */ |
| |
| int |
| gcc::dump_manager:: |
| dump_initialized_p (int phase) const |
| { |
| struct dump_file_info *dfi = get_dump_file_info (phase); |
| return dfi->pstate > 0 || dfi->alt_state > 0; |
| } |
| |
| /* Returns the switch name of PHASE. */ |
| |
| const char * |
| dump_flag_name (int phase) |
| { |
| return g->get_dumps ()->dump_flag_name (phase); |
| } |
| |
| const char * |
| gcc::dump_manager:: |
| dump_flag_name (int phase) const |
| { |
| struct dump_file_info *dfi = get_dump_file_info (phase); |
| return dfi->swtch; |
| } |
| |
| /* Finish a tree dump for PHASE. STREAM is the stream created by |
| dump_begin. */ |
| |
| void |
| dump_end (int phase ATTRIBUTE_UNUSED, FILE *stream) |
| { |
| if (stream != stderr && stream != stdout) |
| fclose (stream); |
| } |
| |
| /* Enable all tree dumps with FLAGS on FILENAME. Return number of |
| enabled tree dumps. */ |
| |
| int |
| gcc::dump_manager:: |
| dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename) |
| { |
| int n = 0; |
| size_t i; |
| |
| for (i = TDI_none + 1; i < (size_t) TDI_end; i++) |
| { |
| if ((dump_files[i].dkind == dkind)) |
| { |
| const char *old_filename = dump_files[i].pfilename; |
| dump_files[i].pstate = -1; |
| dump_files[i].pflags |= flags; |
| n++; |
| /* Override the existing filename. */ |
| if (filename) |
| { |
| dump_files[i].pfilename = xstrdup (filename); |
| /* Since it is a command-line provided file, which is |
| common to all the phases, use it in append mode. */ |
| dump_files[i].pstate = 1; |
| } |
| if (old_filename && filename != old_filename) |
| free (CONST_CAST (char *, old_filename)); |
| } |
| } |
| |
| for (i = 0; i < m_extra_dump_files_in_use; i++) |
| { |
| if ((m_extra_dump_files[i].dkind == dkind)) |
| { |
| const char *old_filename = m_extra_dump_files[i].pfilename; |
| m_extra_dump_files[i].pstate = -1; |
| m_extra_dump_files[i].pflags |= flags; |
| n++; |
| /* Override the existing filename. */ |
| if (filename) |
| { |
| m_extra_dump_files[i].pfilename = xstrdup (filename); |
| /* Since it is a command-line provided file, which is |
| common to all the phases, use it in append mode. */ |
| m_extra_dump_files[i].pstate = 1; |
| } |
| if (old_filename && filename != old_filename) |
| free (CONST_CAST (char *, old_filename)); |
| } |
| } |
| |
| return n; |
| } |
| |
| /* Enable -fopt-info dumps on all dump files matching OPTGROUP_FLAGS. |
| Enable dumps with FLAGS on FILENAME. Return the number of enabled |
| dumps. */ |
| |
| int |
| gcc::dump_manager:: |
| opt_info_enable_passes (int optgroup_flags, dump_flags_t flags, |
| const char *filename) |
| { |
| int n = 0; |
| size_t i; |
| |
| for (i = TDI_none + 1; i < (size_t) TDI_end; i++) |
| { |
| if ((dump_files[i].optgroup_flags & optgroup_flags)) |
| { |
| const char *old_filename = dump_files[i].alt_filename; |
| /* Since this file is shared among different passes, it |
| should be opened in append mode. */ |
| dump_files[i].alt_state = 1; |
| dump_files[i].alt_flags |= flags; |
| n++; |
| /* Override the existing filename. */ |
| if (filename) |
| dump_files[i].alt_filename = xstrdup (filename); |
| if (old_filename && filename != old_filename) |
| free (CONST_CAST (char *, old_filename)); |
| } |
| } |
| |
| for (i = 0; i < m_extra_dump_files_in_use; i++) |
| { |
| if ((m_extra_dump_files[i].optgroup_flags & optgroup_flags)) |
| { |
| const char *old_filename = m_extra_dump_files[i].alt_filename; |
| /* Since this file is shared among different passes, it |
| should be opened in append mode. */ |
| m_extra_dump_files[i].alt_state = 1; |
| m_extra_dump_files[i].alt_flags |= flags; |
| n++; |
| /* Override the existing filename. */ |
| if (filename) |
| m_extra_dump_files[i].alt_filename = xstrdup (filename); |
| if (old_filename && filename != old_filename) |
| free (CONST_CAST (char *, old_filename)); |
| } |
| } |
| |
| return n; |
| } |
| |
| /* Parse ARG as a dump switch. Return nonzero if it is, and store the |
| relevant details in the dump_files array. */ |
| |
| int |
| gcc::dump_manager:: |
| dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob) |
| { |
| const char *option_value; |
| const char *ptr; |
| dump_flags_t flags; |
| |
| if (doglob && !dfi->glob) |
| return 0; |
| |
| option_value = skip_leading_substring (arg, doglob ? dfi->glob : dfi->swtch); |
| if (!option_value) |
| return 0; |
| |
| if (*option_value && *option_value != '-' && *option_value != '=') |
| return 0; |
| |
| ptr = option_value; |
| flags = 0; |
| |
| while (*ptr) |
| { |
| const struct dump_option_value_info *option_ptr; |
| const char *end_ptr; |
| const char *eq_ptr; |
| unsigned length; |
| |
| while (*ptr == '-') |
| ptr++; |
| end_ptr = strchr (ptr, '-'); |
| eq_ptr = strchr (ptr, '='); |
| |
| if (eq_ptr && !end_ptr) |
| end_ptr = eq_ptr; |
| |
| if (!end_ptr) |
| end_ptr = ptr + strlen (ptr); |
| length = end_ptr - ptr; |
| |
| for (option_ptr = dump_options; option_ptr->name; option_ptr++) |
| if (strlen (option_ptr->name) == length |
| && !memcmp (option_ptr->name, ptr, length)) |
| { |
| flags |= option_ptr->value; |
| goto found; |
| } |
| |
| if (*ptr == '=') |
| { |
| /* Interpret rest of the argument as a dump filename. This |
| filename overrides other command line filenames. */ |
| if (dfi->pfilename) |
| free (CONST_CAST (char *, dfi->pfilename)); |
| dfi->pfilename = xstrdup (ptr + 1); |
| break; |
| } |
| else |
| warning (0, "ignoring unknown option %q.*s in %<-fdump-%s%>", |
| length, ptr, dfi->swtch); |
| found:; |
| ptr = end_ptr; |
| } |
| |
| dfi->pstate = -1; |
| dfi->pflags |= flags; |
| |
| /* Process -fdump-tree-all and -fdump-rtl-all, by enabling all the |
| known dumps. */ |
| if (dfi->suffix == NULL) |
| dump_enable_all (dfi->dkind, dfi->pflags, dfi->pfilename); |
| |
| return 1; |
| } |
| |
| int |
| gcc::dump_manager:: |
| dump_switch_p (const char *arg) |
| { |
| size_t i; |
| int any = 0; |
| |
| for (i = TDI_none + 1; i != TDI_end; i++) |
| any |= dump_switch_p_1 (arg, &dump_files[i], false); |
| |
| /* Don't glob if we got a hit already */ |
| if (!any) |
| for (i = TDI_none + 1; i != TDI_end; i++) |
| any |= dump_switch_p_1 (arg, &dump_files[i], true); |
| |
| for (i = 0; i < m_extra_dump_files_in_use; i++) |
| any |= dump_switch_p_1 (arg, &m_extra_dump_files[i], false); |
| |
| if (!any) |
| for (i = 0; i < m_extra_dump_files_in_use; i++) |
| any |= dump_switch_p_1 (arg, &m_extra_dump_files[i], true); |
| |
| |
| return any; |
| } |
| |
| /* Parse ARG as a -fopt-info switch and store flags, optgroup_flags |
| and filename. Return non-zero if it is a recognized switch. */ |
| |
| static int |
| opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags, |
| char **filename) |
| { |
| const char *option_value; |
| const char *ptr; |
| |
| option_value = arg; |
| ptr = option_value; |
| |
| *filename = NULL; |
| *flags = 0; |
| *optgroup_flags = 0; |
| |
| if (!ptr) |
| return 1; /* Handle '-fopt-info' without any additional options. */ |
| |
| while (*ptr) |
| { |
| const struct dump_option_value_info *option_ptr; |
| const char *end_ptr; |
| const char *eq_ptr; |
| unsigned length; |
| |
| while (*ptr == '-') |
| ptr++; |
| end_ptr = strchr (ptr, '-'); |
| eq_ptr = strchr (ptr, '='); |
| |
| if (eq_ptr && !end_ptr) |
| end_ptr = eq_ptr; |
| |
| if (!end_ptr) |
| end_ptr = ptr + strlen (ptr); |
| length = end_ptr - ptr; |
| |
| for (option_ptr = optinfo_verbosity_options; option_ptr->name; |
| option_ptr++) |
| if (strlen (option_ptr->name) == length |
| && !memcmp (option_ptr->name, ptr, length)) |
| { |
| *flags |= option_ptr->value; |
| goto found; |
| } |
| |
| for (option_ptr = optgroup_options; option_ptr->name; option_ptr++) |
| if (strlen (option_ptr->name) == length |
| && !memcmp (option_ptr->name, ptr, length)) |
| { |
| *optgroup_flags |= option_ptr->value; |
| goto found; |
| } |
| |
| if (*ptr == '=') |
| { |
| /* Interpret rest of the argument as a dump filename. This |
| filename overrides other command line filenames. */ |
| *filename = xstrdup (ptr + 1); |
| break; |
| } |
| else |
| { |
| warning (0, "unknown option %q.*s in %<-fopt-info-%s%>", |
| length, ptr, arg); |
| return 0; |
| } |
| found:; |
| ptr = end_ptr; |
| } |
| |
| return 1; |
| } |
| |
| /* Return non-zero if ARG is a recognized switch for |
| -fopt-info. Return zero otherwise. */ |
| |
| int |
| opt_info_switch_p (const char *arg) |
| { |
| dump_flags_t flags; |
| int optgroup_flags; |
| char *filename; |
| static char *file_seen = NULL; |
| gcc::dump_manager *dumps = g->get_dumps (); |
| |
| if (!opt_info_switch_p_1 (arg, &flags, &optgroup_flags, &filename)) |
| return 0; |
| |
| if (!filename) |
| filename = xstrdup ("stderr"); |
| |
| /* Bail out if a different filename has been specified. */ |
| if (file_seen && strcmp (file_seen, filename)) |
| { |
| warning (0, "ignoring possibly conflicting option %<-fopt-info-%s%>", |
| arg); |
| return 1; |
| } |
| |
| file_seen = xstrdup (filename); |
| if (!flags) |
| flags = MSG_OPTIMIZED_LOCATIONS; |
| if (!optgroup_flags) |
| optgroup_flags = OPTGROUP_ALL; |
| |
| return dumps->opt_info_enable_passes (optgroup_flags, flags, filename); |
| } |
| |
| /* Print basic block on the dump streams. */ |
| |
| void |
| dump_basic_block (int dump_kind, basic_block bb, int indent) |
| { |
| if (dump_file && (dump_kind & pflags)) |
| dump_bb (dump_file, bb, indent, TDF_DETAILS); |
| if (alt_dump_file && (dump_kind & alt_flags)) |
| dump_bb (alt_dump_file, bb, indent, TDF_DETAILS); |
| } |
| |
| /* Dump FUNCTION_DECL FN as tree dump PHASE. */ |
| |
| void |
| dump_function (int phase, tree fn) |
| { |
| FILE *stream; |
| dump_flags_t flags; |
| |
| stream = dump_begin (phase, &flags); |
| if (stream) |
| { |
| dump_function_to_file (fn, stream, flags); |
| dump_end (phase, stream); |
| } |
| } |
| |
| /* Print information from the combine pass on dump_file. */ |
| |
| void |
| print_combine_total_stats (void) |
| { |
| if (dump_file) |
| dump_combine_total_stats (dump_file); |
| } |
| |
| /* Enable RTL dump for all the RTL passes. */ |
| |
| bool |
| enable_rtl_dump_file (void) |
| { |
| gcc::dump_manager *dumps = g->get_dumps (); |
| int num_enabled = |
| dumps->dump_enable_all (DK_rtl, dump_flags_t (TDF_DETAILS) | TDF_BLOCKS, |
| NULL); |
| return num_enabled > 0; |
| } |