mirror of git://gcc.gnu.org/git/gcc.git
1059 lines
28 KiB
C
1059 lines
28 KiB
C
/* 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
|
|
<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 (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);
|
|
}
|
|
|
|
/* Open a dump file called FILENAME. Some filenames are special and
|
|
refer to the standard streams. TRUNC indicates whether this is the
|
|
first open (so the file should be truncated, rather than appended).
|
|
An error message is emitted in the event of failure. */
|
|
|
|
static FILE *
|
|
dump_open (const char *filename, bool trunc)
|
|
{
|
|
if (strcmp ("stderr", filename) == 0)
|
|
return stderr;
|
|
|
|
if (strcmp ("stdout", filename) == 0
|
|
|| strcmp ("-", filename) == 0)
|
|
return stdout;
|
|
|
|
FILE *stream = fopen (filename, trunc ? "w" : "a");
|
|
|
|
if (!stream)
|
|
error ("could not open dump file %qs: %m", filename);
|
|
return stream;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
if (!dfi->alt_filename)
|
|
return NULL;
|
|
|
|
if (dfi->alt_stream)
|
|
return dfi->alt_stream;
|
|
|
|
FILE *stream = dump_open (dfi->alt_filename, dfi->alt_state < 0);
|
|
|
|
if (stream)
|
|
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<unsigned int N, typename C>
|
|
void
|
|
dump_dec (int dump_kind, const poly_int<N, C> &value)
|
|
{
|
|
STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
|
|
signop sgn = poly_coeff_traits<C>::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 = dump_open (name, dfi->pstate < 0);
|
|
if (stream)
|
|
{
|
|
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->pstream != stdout && dfi->pstream != stderr)
|
|
fclose (dfi->pstream);
|
|
|
|
if (dfi->alt_stream && dfi->alt_stream != stdout && dfi->alt_stream != stderr)
|
|
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 = dump_open (name, dfi->pstate < 0);
|
|
if (stream)
|
|
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;
|
|
}
|