/* Dump infrastructure for optimizations and intermediate representation. Copyright (C) 2012-2018 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 . */ #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 (dfi->suffix)); XDELETEVEC (const_cast (dfi->swtch)); XDELETEVEC (const_cast (dfi->glob)); } /* These, if non-NULL, are always dynamically allocated. */ XDELETEVEC (const_cast (dfi->pfilename)); XDELETEVEC (const_cast (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 (strcmp (m_extra_dump_files[i].swtch, swtch) == 0) 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); } } /* Output VALUE in decimal to appropriate dump streams. */ template void dump_dec (int dump_kind, const poly_int &value) { STATIC_ASSERT (poly_coeff_traits::signedness >= 0); signop sgn = poly_coeff_traits::signedness ? SIGNED : UNSIGNED; if (dump_file && (dump_kind & pflags)) print_dec (value, dump_file, sgn); if (alt_dump_file && (dump_kind & alt_flags)) print_dec (value, alt_dump_file, sgn); } template void dump_dec (int, const poly_uint16 &); template void dump_dec (int, const poly_int64 &); template void dump_dec (int, const poly_uint64 &); template void dump_dec (int, const poly_offset_int &); template void dump_dec (int, const poly_widest_int &); /* 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; }