mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
This patch adds a new key/value pair "cfgs={yes,no}" to diagnostics
sinks, "no" by default.
If set to "yes" for a SARIF sink, then GCC will add the internal state
of the CFG for all functions after each pertinent optimization pass in
graph form to theRun.graphs in the SARIF output.
If set to "yes" for an HTML sink, the generated HTML will contain SVG
displaying the graphs, adapted from code in graph.cc
Text sinks ignore it.
The SARIF output is thus a machine-readable serialization of (some of)
GCC's intermediate representation (as JSON), but it's much less than
GCC-XML used to provide. The precise form of the information is
documented as subject to change without notice.
Currently it shows both gimple statements and RTL instructions,
depending on the pass. My hope is that it should be possible to write a
"cfg-grep" tool that can read the SARIF and automatically identify
in which pass a particular piece of our IR appeared or disappeared,
for tracking down bugs in our optimization passes.
Implementation-wise:
* this uses the publish-subscribe mechanism from the earlier patch, by
having the diagnostics sink subscribe to pass_events::after_pass
messages from the pass_events_channel.
* the patch adds a new hook to cfghooks.h for dumping a basic block
into a SARIF property bag
gcc/ChangeLog:
* Makefile.in (OBJS): Add tree-diagnostic-cfg.o.
(OBJS-libcommon): Add custom-sarif-properties/cfg.o,
diagnostics/digraphs-to-dot.o, and
diagnostics/digraphs-to-dot-from-cfg.o.
* cfghooks.cc: Define INCLUDE_VECTOR. Add includes of
"diagnostics/sarif-sink.h" and "custom-sarif-properties/cfg.h".
(dump_bb_as_sarif_properties): New.
* cfghooks.h (diagnostics::sarif_builder): New forward decl.
(json::object): New forward decl.
(cfg_hooks::dump_bb_as_sarif_properties): New callback field.
(dump_bb_as_sarif_properties): New decl.
* cfgrtl.cc (rtl_cfg_hooks): Populate the new callback
field with rtl_dump_bb_as_sarif_properties.
(cfg_layout_rtl_cfg_hooks): Likewise.
* custom-sarif-properties/cfg.cc: New file.
* custom-sarif-properties/cfg.h: New file.
* diagnostics/digraphs-to-dot-from-cfg.cc: New file, partly
adapted from gcc/graph.cc.
* diagnostics/digraphs-to-dot.cc: New file.
* diagnostics/digraphs-to-dot.h: New file, based on material in...
* diagnostics/digraphs.cc: Include
"diagnostics/digraphs-to-dot.h".
(class conversion_to_dot): Rework and move to above.
(make_dot_graph_from_diagnostic_graph): Likewise.
(make_dot_node_from_digraph_node): Likewise.
(make_dot_edge_from_digraph_edge): Likewise.
(conversion_to_dot::get_dot_id_for_node): Likewise.
(conversion_to_dot::has_edges_p): Likewise.
(digraph::make_dot_graph): Use to_dot::converter::make and invoke
the result to make the dot graph.
* diagnostics/digraphs.h (digraph:get_all_nodes): New accessor.
* diagnostics/html-sink.cc
(html_builder::m_per_logical_loc_graphs): New field.
(html_builder::add_graph_for_logical_loc): New.
(html_sink::report_digraph_for_logical_location): New.
* diagnostics/sarif-sink.cc (sarif_array_of_unique::get_element):
New.
(sarif_builder::report_digraph_for_logical_location): New.
(sarif_sink::report_digraph_for_logical_location): New.
* diagnostics/sink.h: Include "diagnostics/logical-locations.h".
(sink::report_digraph_for_logical_location): New vfunc.
* diagnostics/text-sink.h
(text_sink::report_digraph_for_logical_location): New.
* doc/invoke.texi (fdiagnostics-add-output): Clarify wording.
Distinguish between scheme-specific vs GCC-specific keys, and add
"cfgs" as the first example of the latter.
* gimple-pretty-print.cc: Include "cfghooks.h", "json.h", and
"custom-sarif-properties/cfg.h".
(gimple_dump_bb_as_sarif_properties): New.
* gimple-pretty-print.h (diagnostics::sarif_builder): New forward
decl.
(json::object): Likewise.
(gimple_dump_bb_as_sarif_properties): New.
* graphviz.cc (get_compass_pt_from_string): New
* graphviz.h (get_compass_pt_from_string): New decl.
* libsarifreplay.cc (sarif_replayer::handle_graph_object): Fix
overlong line.
* opts-common.cc: Define INCLUDE_VECTOR.
* opts-diagnostic.cc: Define INCLUDE_LIST. Include
"diagnostics/sarif-sink.h", "tree-diagnostic-sink-extensions.h",
"opts-diagnostic.h", and "pub-sub.h".
(class gcc_extra_keys): New class.
(opt_spec_context::opt_spec_context): Add "client_keys" param and
pass to dc_spec_context.
(handle_gcc_specific_keys): New.
(try_to_make_sink): New.
(gcc_extension_factory::singleton): New.
(handle_OPT_fdiagnostics_add_output_): Rework to use
try_to_make_sink.
(handle_OPT_fdiagnostics_set_output_): Likewise.
* opts-diagnostic.h: Include "diagnostics/sink.h".
(class gcc_extension_factory): New.
* opts.cc: Define INCLUDE_LIST.
* print-rtl.cc: Include "dumpfile.h", "cfghooks.h", "json.h", and
"custom-sarif-properties/cfg.h".
(rtl_dump_bb_as_sarif_properties): New.
* print-rtl.h (diagnostics::sarif_builder): New forward decl.
(json::object): Likewise.
(rtl_dump_bb_as_sarif_properties): New decl.
* tree-cfg.cc (gimple_cfg_hooks): Use
gimple_dump_bb_as_sarif_properties for new callback field.
* tree-diagnostic-cfg.cc: New file, based on material in graph.cc.
* tree-diagnostic-sink-extensions.h: New file.
* tree-diagnostic.cc: Define INCLUDE_LIST. Include
"tree-diagnostic-sink-extensions.h".
(compiler_ext_factory): New.
(tree_diagnostics_defaults): Set gcc_extension_factory::singleton
to be compiler_ext_factory.
gcc/testsuite/ChangeLog:
* gcc.dg/diagnostic-cfgs-html.py: New test.
* gcc.dg/diagnostic-cfgs-sarif.py: New test.
* gcc.dg/diagnostic-cfgs.c: New test.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2226 lines
54 KiB
C++
2226 lines
54 KiB
C++
/* Print RTL for GCC.
|
|
Copyright (C) 1987-2026 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/>. */
|
|
|
|
/* This file is compiled twice: once for the generator programs,
|
|
once for the compiler. */
|
|
#ifdef GENERATOR_FILE
|
|
#include "bconfig.h"
|
|
#else
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "rtl.h"
|
|
|
|
/* These headers all define things which are not available in
|
|
generator programs. */
|
|
#ifndef GENERATOR_FILE
|
|
#include "alias.h"
|
|
#include "tree.h"
|
|
#include "basic-block.h"
|
|
#include "print-tree.h"
|
|
#include "flags.h"
|
|
#include "predict.h"
|
|
#include "function.h"
|
|
#include "cfg.h"
|
|
#include "basic-block.h"
|
|
#include "diagnostic.h"
|
|
#include "tree-pretty-print.h"
|
|
#include "alloc-pool.h"
|
|
#include "cselib.h"
|
|
#include "dumpfile.h" /* for dump_flags */
|
|
#include "dwarf2out.h"
|
|
#include "pretty-print.h"
|
|
#endif
|
|
|
|
#include "dumpfile.h"
|
|
#include "cfghooks.h"
|
|
#include "print-rtl.h"
|
|
#include "rtl-iter.h"
|
|
#include "json.h"
|
|
|
|
#include "custom-sarif-properties/cfg.h"
|
|
|
|
/* Disable warnings about quoting issues in the pp_xxx calls below
|
|
that (intentionally) don't follow GCC diagnostic conventions. */
|
|
#if __GNUC__ >= 10
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wformat-diag"
|
|
#endif
|
|
|
|
/* String printed at beginning of each RTL when it is dumped.
|
|
This string is set to ASM_COMMENT_START when the RTL is dumped in
|
|
the assembly output file. */
|
|
const char *print_rtx_head = "";
|
|
|
|
#ifdef GENERATOR_FILE
|
|
/* These are defined from the .opt file when not used in generator
|
|
programs. */
|
|
|
|
/* Nonzero means suppress output of instruction numbers
|
|
in debugging dumps.
|
|
This must be defined here so that programs like gencodes can be linked. */
|
|
int flag_dump_unnumbered = 0;
|
|
|
|
/* Nonzero means suppress output of instruction numbers for previous
|
|
and next insns in debugging dumps.
|
|
This must be defined here so that programs like gencodes can be linked. */
|
|
int flag_dump_unnumbered_links = 0;
|
|
#endif
|
|
|
|
/* Constructor for rtx_writer. */
|
|
|
|
rtx_writer::rtx_writer (FILE *outf, int ind, bool simple, bool compact,
|
|
rtx_reuse_manager *reuse_manager ATTRIBUTE_UNUSED)
|
|
: m_outfile (outf), m_indent (ind), m_sawclose (false),
|
|
m_in_call_function_usage (false), m_simple (simple), m_compact (compact)
|
|
#ifndef GENERATOR_FILE
|
|
, m_rtx_reuse_manager (reuse_manager)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
#ifndef GENERATOR_FILE
|
|
|
|
/* rtx_reuse_manager's ctor. */
|
|
|
|
rtx_reuse_manager::rtx_reuse_manager ()
|
|
: m_next_id (0)
|
|
{
|
|
}
|
|
|
|
/* Determine if X is of a kind suitable for dumping via reuse_rtx. */
|
|
|
|
static bool
|
|
uses_rtx_reuse_p (const_rtx x)
|
|
{
|
|
if (x == NULL)
|
|
return false;
|
|
|
|
switch (GET_CODE (x))
|
|
{
|
|
case DEBUG_EXPR:
|
|
case VALUE:
|
|
case SCRATCH:
|
|
return true;
|
|
|
|
/* We don't use reuse_rtx for consts. */
|
|
CASE_CONST_UNIQUE:
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Traverse X and its descendents, determining if we see any rtx more than
|
|
once. Any rtx suitable for "reuse_rtx" that is seen more than once is
|
|
assigned an ID. */
|
|
|
|
void
|
|
rtx_reuse_manager::preprocess (const_rtx x)
|
|
{
|
|
subrtx_iterator::array_type array;
|
|
FOR_EACH_SUBRTX (iter, array, x, NONCONST)
|
|
if (uses_rtx_reuse_p (*iter))
|
|
{
|
|
if (int *count = m_rtx_occurrence_count.get (*iter))
|
|
{
|
|
if (*(count++) == 1)
|
|
m_rtx_reuse_ids.put (*iter, m_next_id++);
|
|
}
|
|
else
|
|
m_rtx_occurrence_count.put (*iter, 1);
|
|
}
|
|
}
|
|
|
|
/* Return true iff X has been assigned a reuse ID. If it has,
|
|
and OUT is non-NULL, then write the reuse ID to *OUT. */
|
|
|
|
bool
|
|
rtx_reuse_manager::has_reuse_id (const_rtx x, int *out)
|
|
{
|
|
int *id = m_rtx_reuse_ids.get (x);
|
|
if (id)
|
|
{
|
|
if (out)
|
|
*out = *id;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Determine if set_seen_def has been called for the given reuse ID. */
|
|
|
|
bool
|
|
rtx_reuse_manager::seen_def_p (int reuse_id)
|
|
{
|
|
return bitmap_bit_p (m_defs_seen, reuse_id);
|
|
}
|
|
|
|
/* Record that the definition of the given reuse ID has been seen. */
|
|
|
|
void
|
|
rtx_reuse_manager::set_seen_def (int reuse_id)
|
|
{
|
|
bitmap_set_bit (m_defs_seen, reuse_id);
|
|
}
|
|
|
|
#endif /* #ifndef GENERATOR_FILE */
|
|
|
|
#ifndef GENERATOR_FILE
|
|
void
|
|
print_mem_expr (FILE *outfile, const_tree expr)
|
|
{
|
|
fputc (' ', outfile);
|
|
print_generic_expr (outfile, CONST_CAST_TREE (expr),
|
|
dump_flags | TDF_SLIM);
|
|
}
|
|
#endif
|
|
|
|
/* Print X to FILE. */
|
|
|
|
static void
|
|
print_poly_int (FILE *file, poly_int64 x)
|
|
{
|
|
HOST_WIDE_INT const_x;
|
|
if (x.is_constant (&const_x))
|
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, const_x);
|
|
else
|
|
{
|
|
fprintf (file, "[" HOST_WIDE_INT_PRINT_DEC, x.coeffs[0]);
|
|
for (int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
|
|
fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC, x.coeffs[i]);
|
|
fprintf (file, "]");
|
|
}
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code '0'.
|
|
0 indicates a field for internal use that should not be printed.
|
|
However there are various special cases, such as the third field
|
|
of a NOTE, where it indicates that the field has several different
|
|
valid contents. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_0 (const_rtx in_rtx ATTRIBUTE_UNUSED,
|
|
int idx ATTRIBUTE_UNUSED)
|
|
{
|
|
#ifndef GENERATOR_FILE
|
|
if (idx == 1 && GET_CODE (in_rtx) == SYMBOL_REF)
|
|
{
|
|
int flags = SYMBOL_REF_FLAGS (in_rtx);
|
|
if (flags)
|
|
fprintf (m_outfile, " [flags %#x]", flags);
|
|
tree decl = SYMBOL_REF_DECL (in_rtx);
|
|
if (decl)
|
|
print_node_brief (m_outfile, "", decl, dump_flags);
|
|
}
|
|
else if (idx == 3 && NOTE_P (in_rtx))
|
|
{
|
|
switch (NOTE_KIND (in_rtx))
|
|
{
|
|
case NOTE_INSN_EH_REGION_BEG:
|
|
case NOTE_INSN_EH_REGION_END:
|
|
if (flag_dump_unnumbered)
|
|
fprintf (m_outfile, " #");
|
|
else
|
|
fprintf (m_outfile, " %d", NOTE_EH_HANDLER (in_rtx));
|
|
m_sawclose = true;
|
|
break;
|
|
|
|
case NOTE_INSN_BLOCK_BEG:
|
|
case NOTE_INSN_BLOCK_END:
|
|
dump_addr (m_outfile, " ", NOTE_BLOCK (in_rtx));
|
|
m_sawclose = true;
|
|
break;
|
|
|
|
case NOTE_INSN_BASIC_BLOCK:
|
|
{
|
|
basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
|
|
if (bb != 0)
|
|
fprintf (m_outfile, " [bb %d]", bb->index);
|
|
break;
|
|
}
|
|
|
|
case NOTE_INSN_DELETED_LABEL:
|
|
case NOTE_INSN_DELETED_DEBUG_LABEL:
|
|
{
|
|
const char *label = NOTE_DELETED_LABEL_NAME (in_rtx);
|
|
if (label)
|
|
fprintf (m_outfile, " (\"%s\")", label);
|
|
else
|
|
fprintf (m_outfile, " \"\"");
|
|
}
|
|
break;
|
|
|
|
case NOTE_INSN_SWITCH_TEXT_SECTIONS:
|
|
{
|
|
basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
|
|
if (bb != 0)
|
|
fprintf (m_outfile, " [bb %d]", bb->index);
|
|
break;
|
|
}
|
|
|
|
case NOTE_INSN_VAR_LOCATION:
|
|
fputc (' ', m_outfile);
|
|
print_rtx (NOTE_VAR_LOCATION (in_rtx));
|
|
break;
|
|
|
|
case NOTE_INSN_CFI:
|
|
fputc ('\n', m_outfile);
|
|
output_cfi_directive (m_outfile, NOTE_CFI (in_rtx));
|
|
fputc ('\t', m_outfile);
|
|
break;
|
|
|
|
case NOTE_INSN_BEGIN_STMT:
|
|
case NOTE_INSN_INLINE_ENTRY:
|
|
#ifndef GENERATOR_FILE
|
|
{
|
|
expanded_location xloc
|
|
= expand_location (NOTE_MARKER_LOCATION (in_rtx));
|
|
fprintf (m_outfile, " %s:%i", xloc.file, xloc.line);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (idx == 7 && JUMP_P (in_rtx) && JUMP_LABEL (in_rtx) != NULL
|
|
&& !m_compact)
|
|
{
|
|
/* Output the JUMP_LABEL reference. */
|
|
fprintf (m_outfile, "\n%s%*s -> ", print_rtx_head, m_indent * 2, "");
|
|
if (GET_CODE (JUMP_LABEL (in_rtx)) == RETURN)
|
|
fprintf (m_outfile, "return");
|
|
else if (GET_CODE (JUMP_LABEL (in_rtx)) == SIMPLE_RETURN)
|
|
fprintf (m_outfile, "simple_return");
|
|
else
|
|
fprintf (m_outfile, "%d", INSN_UID (JUMP_LABEL (in_rtx)));
|
|
}
|
|
else if (idx == 0 && GET_CODE (in_rtx) == VALUE)
|
|
{
|
|
cselib_val *val = CSELIB_VAL_PTR (in_rtx);
|
|
|
|
fprintf (m_outfile, " %u:%u", val->uid, val->hash);
|
|
dump_addr (m_outfile, " @", in_rtx);
|
|
dump_addr (m_outfile, "/", (void*)val);
|
|
}
|
|
else if (idx == 0 && GET_CODE (in_rtx) == DEBUG_EXPR)
|
|
{
|
|
fprintf (m_outfile, " D#%i",
|
|
DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (in_rtx)));
|
|
}
|
|
else if (idx == 0 && GET_CODE (in_rtx) == ENTRY_VALUE)
|
|
{
|
|
m_indent += 2;
|
|
if (!m_sawclose)
|
|
fprintf (m_outfile, " ");
|
|
print_rtx (ENTRY_VALUE_EXP (in_rtx));
|
|
m_indent -= 2;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code 'e'.
|
|
Also called by print_rtx_operand_code_u for handling code 'u'
|
|
for LABEL_REFs when they don't reference a CODE_LABEL. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_e (const_rtx in_rtx, int idx)
|
|
{
|
|
m_indent += 2;
|
|
if (idx == 6 && INSN_P (in_rtx))
|
|
/* Put REG_NOTES on their own line. */
|
|
fprintf (m_outfile, "\n%s%*s",
|
|
print_rtx_head, m_indent * 2, "");
|
|
if (!m_sawclose)
|
|
fprintf (m_outfile, " ");
|
|
if (idx == 7 && CALL_P (in_rtx))
|
|
{
|
|
m_in_call_function_usage = true;
|
|
print_rtx (XEXP (in_rtx, idx));
|
|
m_in_call_function_usage = false;
|
|
}
|
|
else
|
|
print_rtx (XEXP (in_rtx, idx));
|
|
m_indent -= 2;
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling codes 'E' and 'V'. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_codes_E_and_V (const_rtx in_rtx, int idx)
|
|
{
|
|
m_indent += 2;
|
|
if (m_sawclose)
|
|
{
|
|
fprintf (m_outfile, "\n%s%*s",
|
|
print_rtx_head, m_indent * 2, "");
|
|
m_sawclose = false;
|
|
}
|
|
if (GET_CODE (in_rtx) == CONST_VECTOR
|
|
&& !GET_MODE_NUNITS (GET_MODE (in_rtx)).is_constant ()
|
|
&& CONST_VECTOR_DUPLICATE_P (in_rtx))
|
|
fprintf (m_outfile, " repeat");
|
|
fputs (" [", m_outfile);
|
|
if (XVEC (in_rtx, idx) != NULL)
|
|
{
|
|
m_indent += 2;
|
|
if (XVECLEN (in_rtx, idx))
|
|
m_sawclose = true;
|
|
|
|
int barrier = XVECLEN (in_rtx, idx);
|
|
if (GET_CODE (in_rtx) == CONST_VECTOR
|
|
&& !GET_MODE_NUNITS (GET_MODE (in_rtx)).is_constant ())
|
|
barrier = CONST_VECTOR_NPATTERNS (in_rtx);
|
|
|
|
for (int j = 0; j < XVECLEN (in_rtx, idx); j++)
|
|
{
|
|
int j1;
|
|
|
|
if (j == barrier)
|
|
{
|
|
fprintf (m_outfile, "\n%s%*s",
|
|
print_rtx_head, m_indent * 2, "");
|
|
if (!CONST_VECTOR_STEPPED_P (in_rtx))
|
|
fprintf (m_outfile, "repeat [");
|
|
else if (CONST_VECTOR_NPATTERNS (in_rtx) == 1)
|
|
fprintf (m_outfile, "stepped [");
|
|
else
|
|
fprintf (m_outfile, "stepped (interleave %d) [",
|
|
CONST_VECTOR_NPATTERNS (in_rtx));
|
|
m_indent += 2;
|
|
}
|
|
|
|
print_rtx (XVECEXP (in_rtx, idx, j));
|
|
int limit = MIN (barrier, XVECLEN (in_rtx, idx));
|
|
for (j1 = j + 1; j1 < limit; j1++)
|
|
if (XVECEXP (in_rtx, idx, j) != XVECEXP (in_rtx, idx, j1))
|
|
break;
|
|
|
|
if (j1 != j + 1)
|
|
{
|
|
fprintf (m_outfile, " repeated x%i", j1 - j);
|
|
j = j1 - 1;
|
|
}
|
|
}
|
|
|
|
if (barrier < XVECLEN (in_rtx, idx))
|
|
{
|
|
m_indent -= 2;
|
|
fprintf (m_outfile, "\n%s%*s]", print_rtx_head, m_indent * 2, "");
|
|
}
|
|
|
|
m_indent -= 2;
|
|
}
|
|
if (m_sawclose)
|
|
fprintf (m_outfile, "\n%s%*s", print_rtx_head, m_indent * 2, "");
|
|
|
|
fputs ("]", m_outfile);
|
|
m_sawclose = true;
|
|
m_indent -= 2;
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code 'L'. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_L (const_rtx in_rtx, int idx)
|
|
{
|
|
if (idx == 4 && INSN_P (in_rtx))
|
|
{
|
|
#ifndef GENERATOR_FILE
|
|
const rtx_insn *in_insn = as_a <const rtx_insn *> (in_rtx);
|
|
|
|
/* Pretty-print insn locations. Ignore scoping as it is mostly
|
|
redundant with line number information and do not print anything
|
|
when there is no location information available. */
|
|
if (INSN_HAS_LOCATION (in_insn))
|
|
{
|
|
expanded_location xloc = insn_location (in_insn);
|
|
fprintf (m_outfile, " \"%s\":%i:%i", xloc.file, xloc.line,
|
|
xloc.column);
|
|
int discriminator = insn_discriminator (in_insn);
|
|
if (discriminator)
|
|
fprintf (m_outfile, " discrim %d", discriminator);
|
|
|
|
}
|
|
#endif
|
|
}
|
|
else if (idx == 6 && GET_CODE (in_rtx) == ASM_OPERANDS)
|
|
{
|
|
#ifndef GENERATOR_FILE
|
|
if (ASM_OPERANDS_SOURCE_LOCATION (in_rtx) != UNKNOWN_LOCATION)
|
|
fprintf (m_outfile, " %s:%i",
|
|
LOCATION_FILE (ASM_OPERANDS_SOURCE_LOCATION (in_rtx)),
|
|
LOCATION_LINE (ASM_OPERANDS_SOURCE_LOCATION (in_rtx)));
|
|
#endif
|
|
}
|
|
else if (idx == 1 && GET_CODE (in_rtx) == ASM_INPUT)
|
|
{
|
|
#ifndef GENERATOR_FILE
|
|
if (ASM_INPUT_SOURCE_LOCATION (in_rtx) != UNKNOWN_LOCATION)
|
|
fprintf (m_outfile, " %s:%i",
|
|
LOCATION_FILE (ASM_INPUT_SOURCE_LOCATION (in_rtx)),
|
|
LOCATION_LINE (ASM_INPUT_SOURCE_LOCATION (in_rtx)));
|
|
#endif
|
|
}
|
|
else
|
|
gcc_unreachable ();
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code 'i'. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_i (const_rtx in_rtx, int idx)
|
|
{
|
|
if (idx == 5 && NOTE_P (in_rtx))
|
|
{
|
|
/* This field is only used for NOTE_INSN_DELETED_LABEL, and
|
|
other times often contains garbage from INSN->NOTE death. */
|
|
if (NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_LABEL
|
|
|| NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_DEBUG_LABEL)
|
|
fprintf (m_outfile, " %d", XINT (in_rtx, idx));
|
|
}
|
|
#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
|
|
else if (idx == 1
|
|
&& GET_CODE (in_rtx) == UNSPEC_VOLATILE
|
|
&& XINT (in_rtx, 1) >= 0
|
|
&& XINT (in_rtx, 1) < NUM_UNSPECV_VALUES)
|
|
fprintf (m_outfile, " %s", unspecv_strings[XINT (in_rtx, 1)]);
|
|
#endif
|
|
#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
|
|
else if (idx == 1
|
|
&& (GET_CODE (in_rtx) == UNSPEC
|
|
|| GET_CODE (in_rtx) == UNSPEC_VOLATILE)
|
|
&& XINT (in_rtx, 1) >= 0
|
|
&& XINT (in_rtx, 1) < NUM_UNSPEC_VALUES)
|
|
fprintf (m_outfile, " %s", unspec_strings[XINT (in_rtx, 1)]);
|
|
#endif
|
|
else
|
|
{
|
|
int value = XINT (in_rtx, idx);
|
|
const char *name;
|
|
int is_insn = INSN_P (in_rtx);
|
|
|
|
/* Don't print INSN_CODEs in compact mode. */
|
|
if (m_compact && is_insn && &INSN_CODE (in_rtx) == &XINT (in_rtx, idx))
|
|
{
|
|
m_sawclose = false;
|
|
return;
|
|
}
|
|
|
|
if (flag_dump_unnumbered
|
|
&& (is_insn || NOTE_P (in_rtx)))
|
|
fputc ('#', m_outfile);
|
|
else
|
|
fprintf (m_outfile, " %d", value);
|
|
|
|
if (is_insn && &INSN_CODE (in_rtx) == &XINT (in_rtx, idx)
|
|
&& XINT (in_rtx, idx) >= 0
|
|
&& (name = get_insn_name (XINT (in_rtx, idx))) != NULL)
|
|
fprintf (m_outfile, " {%s}", name);
|
|
m_sawclose = false;
|
|
}
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code 'r'. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_r (const_rtx in_rtx)
|
|
{
|
|
int is_insn = INSN_P (in_rtx);
|
|
unsigned int regno = REGNO (in_rtx);
|
|
|
|
#ifndef GENERATOR_FILE
|
|
/* For hard registers and virtuals, always print the
|
|
regno, except in compact mode. */
|
|
if (regno <= LAST_VIRTUAL_REGISTER && !m_compact)
|
|
fprintf (m_outfile, " %d", regno);
|
|
if (regno < FIRST_PSEUDO_REGISTER)
|
|
fprintf (m_outfile, " %s", reg_names[regno]);
|
|
else if (regno <= LAST_VIRTUAL_REGISTER)
|
|
{
|
|
if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
|
|
fprintf (m_outfile, " virtual-incoming-args");
|
|
else if (regno == VIRTUAL_STACK_VARS_REGNUM)
|
|
fprintf (m_outfile, " virtual-stack-vars");
|
|
else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
|
|
fprintf (m_outfile, " virtual-stack-dynamic");
|
|
else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
|
|
fprintf (m_outfile, " virtual-outgoing-args");
|
|
else if (regno == VIRTUAL_CFA_REGNUM)
|
|
fprintf (m_outfile, " virtual-cfa");
|
|
else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
|
|
fprintf (m_outfile, " virtual-preferred-stack-boundary");
|
|
else
|
|
fprintf (m_outfile, " virtual-reg-%d", regno-FIRST_VIRTUAL_REGISTER);
|
|
}
|
|
else
|
|
#endif
|
|
if (flag_dump_unnumbered && is_insn)
|
|
fputc ('#', m_outfile);
|
|
else if (m_compact)
|
|
{
|
|
/* In compact mode, print pseudos with '< and '>' wrapping the regno,
|
|
offseting it by (LAST_VIRTUAL_REGISTER + 1), so that the
|
|
first non-virtual pseudo is dumped as "<0>". */
|
|
gcc_assert (regno > LAST_VIRTUAL_REGISTER);
|
|
fprintf (m_outfile, " <%d>", regno - (LAST_VIRTUAL_REGISTER + 1));
|
|
}
|
|
else
|
|
fprintf (m_outfile, " %d", regno);
|
|
|
|
#ifndef GENERATOR_FILE
|
|
if (REG_ATTRS (in_rtx))
|
|
{
|
|
fputs (" [", m_outfile);
|
|
if (regno != ORIGINAL_REGNO (in_rtx))
|
|
fprintf (m_outfile, "orig:%i", ORIGINAL_REGNO (in_rtx));
|
|
if (REG_EXPR (in_rtx))
|
|
print_mem_expr (m_outfile, REG_EXPR (in_rtx));
|
|
|
|
if (maybe_ne (REG_OFFSET (in_rtx), 0))
|
|
{
|
|
fprintf (m_outfile, "+");
|
|
print_poly_int (m_outfile, REG_OFFSET (in_rtx));
|
|
}
|
|
fputs (" ]", m_outfile);
|
|
}
|
|
if (regno != ORIGINAL_REGNO (in_rtx))
|
|
fprintf (m_outfile, " [%d]", ORIGINAL_REGNO (in_rtx));
|
|
#endif
|
|
}
|
|
|
|
/* Subroutine of print_rtx_operand for handling code 'u'. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand_code_u (const_rtx in_rtx, int idx)
|
|
{
|
|
/* Don't print insn UIDs for PREV/NEXT_INSN in compact mode. */
|
|
if (m_compact && INSN_CHAIN_CODE_P (GET_CODE (in_rtx)) && idx < 2)
|
|
return;
|
|
|
|
if (XEXP (in_rtx, idx) != NULL)
|
|
{
|
|
rtx sub = XEXP (in_rtx, idx);
|
|
enum rtx_code subc = GET_CODE (sub);
|
|
|
|
if (GET_CODE (in_rtx) == LABEL_REF)
|
|
{
|
|
if (subc == NOTE
|
|
&& NOTE_KIND (sub) == NOTE_INSN_DELETED_LABEL)
|
|
{
|
|
if (flag_dump_unnumbered)
|
|
fprintf (m_outfile, " [# deleted]");
|
|
else
|
|
fprintf (m_outfile, " [%d deleted]", INSN_UID (sub));
|
|
m_sawclose = false;
|
|
return;
|
|
}
|
|
|
|
if (subc != CODE_LABEL)
|
|
{
|
|
print_rtx_operand_code_e (in_rtx, idx);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (flag_dump_unnumbered
|
|
|| (flag_dump_unnumbered_links && idx <= 1
|
|
&& (INSN_P (in_rtx) || NOTE_P (in_rtx)
|
|
|| LABEL_P (in_rtx) || BARRIER_P (in_rtx))))
|
|
fputs (" #", m_outfile);
|
|
else
|
|
fprintf (m_outfile, " %d", INSN_UID (sub));
|
|
}
|
|
else
|
|
fputs (" 0", m_outfile);
|
|
m_sawclose = false;
|
|
}
|
|
|
|
/* Subroutine of print_rtx. Print operand IDX of IN_RTX. */
|
|
|
|
void
|
|
rtx_writer::print_rtx_operand (const_rtx in_rtx, int idx)
|
|
{
|
|
const char *format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx));
|
|
|
|
switch (format_ptr[idx])
|
|
{
|
|
const char *str;
|
|
|
|
case 'T':
|
|
str = XTMPL (in_rtx, idx);
|
|
goto string;
|
|
|
|
case 'S':
|
|
case 's':
|
|
str = XSTR (in_rtx, idx);
|
|
string:
|
|
|
|
if (str == 0)
|
|
fputs (" (nil)", m_outfile);
|
|
else
|
|
fprintf (m_outfile, " (\"%s\")", str);
|
|
m_sawclose = true;
|
|
break;
|
|
|
|
case '0':
|
|
print_rtx_operand_code_0 (in_rtx, idx);
|
|
break;
|
|
|
|
case 'e':
|
|
print_rtx_operand_code_e (in_rtx, idx);
|
|
break;
|
|
|
|
case 'E':
|
|
case 'V':
|
|
print_rtx_operand_codes_E_and_V (in_rtx, idx);
|
|
break;
|
|
|
|
case 'w':
|
|
if (! m_simple)
|
|
fprintf (m_outfile, " ");
|
|
fprintf (m_outfile, HOST_WIDE_INT_PRINT_DEC, XWINT (in_rtx, idx));
|
|
if (! m_simple && !m_compact)
|
|
fprintf (m_outfile, " [" HOST_WIDE_INT_PRINT_HEX "]",
|
|
(unsigned HOST_WIDE_INT) XWINT (in_rtx, idx));
|
|
break;
|
|
|
|
case 'i':
|
|
print_rtx_operand_code_i (in_rtx, idx);
|
|
break;
|
|
|
|
case 'L':
|
|
print_rtx_operand_code_L (in_rtx, idx);
|
|
break;
|
|
|
|
case 'p':
|
|
fprintf (m_outfile, " ");
|
|
print_poly_int (m_outfile, SUBREG_BYTE (in_rtx));
|
|
break;
|
|
|
|
case 'r':
|
|
print_rtx_operand_code_r (in_rtx);
|
|
break;
|
|
|
|
/* Print NOTE_INSN names rather than integer codes. */
|
|
|
|
case 'n':
|
|
fprintf (m_outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, idx)));
|
|
m_sawclose = false;
|
|
break;
|
|
|
|
case 'u':
|
|
print_rtx_operand_code_u (in_rtx, idx);
|
|
break;
|
|
|
|
case 't':
|
|
#ifndef GENERATOR_FILE
|
|
if (idx == 0 && GET_CODE (in_rtx) == DEBUG_IMPLICIT_PTR)
|
|
print_mem_expr (m_outfile, DEBUG_IMPLICIT_PTR_DECL (in_rtx));
|
|
else if (idx == 0 && GET_CODE (in_rtx) == DEBUG_PARAMETER_REF)
|
|
print_mem_expr (m_outfile, DEBUG_PARAMETER_REF_DECL (in_rtx));
|
|
else
|
|
dump_addr (m_outfile, " ", XTREE (in_rtx, idx));
|
|
#endif
|
|
break;
|
|
|
|
case '*':
|
|
fputs (" Unknown", m_outfile);
|
|
m_sawclose = false;
|
|
break;
|
|
|
|
case 'B':
|
|
/* Don't print basic block ids in compact mode. */
|
|
if (m_compact)
|
|
break;
|
|
#ifndef GENERATOR_FILE
|
|
if (XBBDEF (in_rtx, idx))
|
|
fprintf (m_outfile, " %i", XBBDEF (in_rtx, idx)->index);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
}
|
|
|
|
/* Subroutine of rtx_writer::print_rtx.
|
|
In compact mode, determine if operand IDX of IN_RTX is interesting
|
|
to dump, or (if in a trailing position) it can be omitted. */
|
|
|
|
bool
|
|
rtx_writer::operand_has_default_value_p (const_rtx in_rtx, int idx)
|
|
{
|
|
const char *format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx));
|
|
|
|
switch (format_ptr[idx])
|
|
{
|
|
case 'e':
|
|
case 'u':
|
|
return XEXP (in_rtx, idx) == NULL_RTX;
|
|
|
|
case 's':
|
|
return XSTR (in_rtx, idx) == NULL;
|
|
|
|
case '0':
|
|
switch (GET_CODE (in_rtx))
|
|
{
|
|
case JUMP_INSN:
|
|
/* JUMP_LABELs are always omitted in compact mode, so treat
|
|
any value here as omittable, so that earlier operands can
|
|
potentially be omitted also. */
|
|
return m_compact;
|
|
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Print IN_RTX onto m_outfile. This is the recursive part of printing. */
|
|
|
|
void
|
|
rtx_writer::print_rtx (const_rtx in_rtx)
|
|
{
|
|
int idx = 0;
|
|
|
|
if (m_sawclose)
|
|
{
|
|
if (m_simple)
|
|
fputc (' ', m_outfile);
|
|
else
|
|
fprintf (m_outfile, "\n%s%*s", print_rtx_head, m_indent * 2, "");
|
|
m_sawclose = false;
|
|
}
|
|
|
|
if (in_rtx == 0)
|
|
{
|
|
fputs ("(nil)", m_outfile);
|
|
m_sawclose = true;
|
|
return;
|
|
}
|
|
else if (GET_CODE (in_rtx) > NUM_RTX_CODE)
|
|
{
|
|
fprintf (m_outfile, "(??? bad code %d\n%s%*s)", GET_CODE (in_rtx),
|
|
print_rtx_head, m_indent * 2, "");
|
|
m_sawclose = true;
|
|
return;
|
|
}
|
|
|
|
fputc ('(', m_outfile);
|
|
|
|
/* Print name of expression code. */
|
|
|
|
/* Handle reuse. */
|
|
#ifndef GENERATOR_FILE
|
|
if (m_rtx_reuse_manager)
|
|
{
|
|
int reuse_id;
|
|
if (m_rtx_reuse_manager->has_reuse_id (in_rtx, &reuse_id))
|
|
{
|
|
/* Have we already seen the defn of this rtx? */
|
|
if (m_rtx_reuse_manager->seen_def_p (reuse_id))
|
|
{
|
|
fprintf (m_outfile, "reuse_rtx %i)", reuse_id);
|
|
m_sawclose = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
/* First time we've seen this reused-rtx. */
|
|
fprintf (m_outfile, "%i|", reuse_id);
|
|
m_rtx_reuse_manager->set_seen_def (reuse_id);
|
|
}
|
|
}
|
|
}
|
|
#endif /* #ifndef GENERATOR_FILE */
|
|
|
|
/* In compact mode, prefix the code of insns with "c",
|
|
giving "cinsn", "cnote" etc. */
|
|
if (m_compact && is_a <const rtx_insn *, const struct rtx_def> (in_rtx))
|
|
{
|
|
/* "ccode_label" is slightly awkward, so special-case it as
|
|
just "clabel". */
|
|
rtx_code code = GET_CODE (in_rtx);
|
|
if (code == CODE_LABEL)
|
|
fprintf (m_outfile, "clabel");
|
|
else
|
|
fprintf (m_outfile, "c%s", GET_RTX_NAME (code));
|
|
}
|
|
else if (m_simple && CONST_INT_P (in_rtx))
|
|
; /* no code. */
|
|
else
|
|
fprintf (m_outfile, "%s", GET_RTX_NAME (GET_CODE (in_rtx)));
|
|
|
|
if (! m_simple)
|
|
{
|
|
if (RTX_FLAG (in_rtx, in_struct))
|
|
fputs ("/s", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, volatil))
|
|
fputs ("/v", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, unchanging))
|
|
fputs ("/u", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, frame_related))
|
|
fputs ("/f", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, jump))
|
|
fputs ("/j", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, call))
|
|
fputs ("/c", m_outfile);
|
|
|
|
if (RTX_FLAG (in_rtx, return_val))
|
|
fputs ("/i", m_outfile);
|
|
|
|
/* Print REG_NOTE names for EXPR_LIST and INSN_LIST. */
|
|
if ((GET_CODE (in_rtx) == EXPR_LIST
|
|
|| GET_CODE (in_rtx) == INSN_LIST
|
|
|| GET_CODE (in_rtx) == INT_LIST)
|
|
&& (int)GET_MODE (in_rtx) < REG_NOTE_MAX
|
|
&& !m_in_call_function_usage)
|
|
fprintf (m_outfile, ":%s",
|
|
GET_REG_NOTE_NAME (GET_MODE (in_rtx)));
|
|
|
|
/* For other rtl, print the mode if it's not VOID. */
|
|
else if (GET_MODE (in_rtx) != VOIDmode)
|
|
fprintf (m_outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx)));
|
|
|
|
#ifndef GENERATOR_FILE
|
|
if (GET_CODE (in_rtx) == VAR_LOCATION)
|
|
{
|
|
if (TREE_CODE (PAT_VAR_LOCATION_DECL (in_rtx)) == STRING_CST)
|
|
fputs (" <debug string placeholder>", m_outfile);
|
|
else
|
|
print_mem_expr (m_outfile, PAT_VAR_LOCATION_DECL (in_rtx));
|
|
fputc (' ', m_outfile);
|
|
print_rtx (PAT_VAR_LOCATION_LOC (in_rtx));
|
|
if (PAT_VAR_LOCATION_STATUS (in_rtx)
|
|
== VAR_INIT_STATUS_UNINITIALIZED)
|
|
fprintf (m_outfile, " [uninit]");
|
|
m_sawclose = true;
|
|
idx = GET_RTX_LENGTH (VAR_LOCATION);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef GENERATOR_FILE
|
|
if (CONST_DOUBLE_AS_FLOAT_P (in_rtx))
|
|
idx = 5;
|
|
#endif
|
|
|
|
/* For insns, print the INSN_UID. */
|
|
if (INSN_CHAIN_CODE_P (GET_CODE (in_rtx)))
|
|
{
|
|
if (flag_dump_unnumbered)
|
|
fprintf (m_outfile, " #");
|
|
else
|
|
fprintf (m_outfile, " %d", INSN_UID (in_rtx));
|
|
}
|
|
|
|
/* Determine which is the final operand to print.
|
|
In compact mode, skip trailing operands that have the default values
|
|
e.g. trailing "(nil)" values. */
|
|
int limit = GET_RTX_LENGTH (GET_CODE (in_rtx));
|
|
if (m_compact)
|
|
while (limit > idx && operand_has_default_value_p (in_rtx, limit - 1))
|
|
limit--;
|
|
|
|
/* Get the format string and skip the first elements if we have handled
|
|
them already. */
|
|
|
|
for (; idx < limit; idx++)
|
|
print_rtx_operand (in_rtx, idx);
|
|
|
|
switch (GET_CODE (in_rtx))
|
|
{
|
|
#ifndef GENERATOR_FILE
|
|
case MEM:
|
|
if (UNLIKELY (final_insns_dump_p))
|
|
fprintf (m_outfile, " [");
|
|
else
|
|
fprintf (m_outfile, " [" HOST_WIDE_INT_PRINT_DEC,
|
|
(HOST_WIDE_INT) MEM_ALIAS_SET (in_rtx));
|
|
|
|
if (MEM_EXPR (in_rtx))
|
|
print_mem_expr (m_outfile, MEM_EXPR (in_rtx));
|
|
else
|
|
fputc (' ', m_outfile);
|
|
|
|
if (MEM_OFFSET_KNOWN_P (in_rtx))
|
|
{
|
|
fprintf (m_outfile, "+");
|
|
print_poly_int (m_outfile, MEM_OFFSET (in_rtx));
|
|
}
|
|
|
|
if (MEM_SIZE_KNOWN_P (in_rtx))
|
|
{
|
|
fprintf (m_outfile, " S");
|
|
print_poly_int (m_outfile, MEM_SIZE (in_rtx));
|
|
}
|
|
|
|
if (MEM_ALIGN (in_rtx) != 1)
|
|
fprintf (m_outfile, " A%u", MEM_ALIGN (in_rtx));
|
|
|
|
if (!ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (in_rtx)))
|
|
fprintf (m_outfile, " AS%u", MEM_ADDR_SPACE (in_rtx));
|
|
|
|
fputc (']', m_outfile);
|
|
break;
|
|
|
|
case CONST_DOUBLE:
|
|
if (FLOAT_MODE_P (GET_MODE (in_rtx)))
|
|
{
|
|
char s[60];
|
|
|
|
real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
|
|
sizeof (s), 0, 1);
|
|
fprintf (m_outfile, " %s", s);
|
|
|
|
real_to_hexadecimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
|
|
sizeof (s), 0, 1);
|
|
fprintf (m_outfile, " [%s]", s);
|
|
}
|
|
break;
|
|
|
|
case CONST_WIDE_INT:
|
|
fprintf (m_outfile, " ");
|
|
cwi_output_hex (m_outfile, in_rtx);
|
|
break;
|
|
|
|
case CONST_POLY_INT:
|
|
fprintf (m_outfile, " [");
|
|
print_dec (CONST_POLY_INT_COEFFS (in_rtx)[0], m_outfile, SIGNED);
|
|
for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
|
|
{
|
|
fprintf (m_outfile, ", ");
|
|
print_dec (CONST_POLY_INT_COEFFS (in_rtx)[i], m_outfile, SIGNED);
|
|
}
|
|
fprintf (m_outfile, "]");
|
|
break;
|
|
#endif
|
|
|
|
case CODE_LABEL:
|
|
if (!m_compact)
|
|
fprintf (m_outfile, " [%d uses]", LABEL_NUSES (in_rtx));
|
|
switch (LABEL_KIND (in_rtx))
|
|
{
|
|
case LABEL_NORMAL: break;
|
|
case LABEL_STATIC_ENTRY: fputs (" [entry]", m_outfile); break;
|
|
case LABEL_GLOBAL_ENTRY: fputs (" [global entry]", m_outfile); break;
|
|
case LABEL_WEAK_ENTRY: fputs (" [weak entry]", m_outfile); break;
|
|
default: gcc_unreachable ();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fputc (')', m_outfile);
|
|
m_sawclose = true;
|
|
}
|
|
|
|
/* Emit a closing parenthesis and newline. */
|
|
|
|
void
|
|
rtx_writer::finish_directive ()
|
|
{
|
|
fprintf (m_outfile, ")\n");
|
|
m_sawclose = false;
|
|
}
|
|
|
|
/* Print an rtx on the current line of FILE. Initially indent IND
|
|
characters. */
|
|
|
|
void
|
|
print_inline_rtx (FILE *outf, const_rtx x, int ind)
|
|
{
|
|
rtx_writer w (outf, ind, false, false, NULL);
|
|
w.print_rtx (x);
|
|
}
|
|
|
|
/* Call this function from the debugger to see what X looks like. */
|
|
|
|
DEBUG_FUNCTION void
|
|
debug_rtx (const_rtx x)
|
|
{
|
|
rtx_writer w (stderr, 0, false, false, NULL);
|
|
w.print_rtx (x);
|
|
fprintf (stderr, "\n");
|
|
}
|
|
|
|
/* Dump rtx REF. */
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (const rtx_def &ref)
|
|
{
|
|
debug_rtx (&ref);
|
|
}
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (const rtx_def *ptr)
|
|
{
|
|
if (ptr)
|
|
debug (*ptr);
|
|
else
|
|
fprintf (stderr, "<nil>\n");
|
|
}
|
|
|
|
/* Like debug_rtx but with no newline, as debug_helper will add one.
|
|
|
|
Note: No debug_slim(rtx_insn *) variant implemented, as this
|
|
function can serve for both rtx and rtx_insn. */
|
|
|
|
static void
|
|
debug_slim (const_rtx x)
|
|
{
|
|
rtx_writer w (stderr, 0, false, false, NULL);
|
|
w.print_rtx (x);
|
|
}
|
|
|
|
DEFINE_DEBUG_VEC (rtx_def *)
|
|
DEFINE_DEBUG_VEC (rtx_insn *)
|
|
DEFINE_DEBUG_HASH_SET (rtx_def *)
|
|
DEFINE_DEBUG_HASH_SET (rtx_insn *)
|
|
|
|
/* Count of rtx's to print with debug_rtx_list.
|
|
This global exists because gdb user defined commands have no arguments. */
|
|
|
|
DEBUG_VARIABLE int debug_rtx_count = 0; /* 0 is treated as equivalent to 1 */
|
|
|
|
/* Call this function to print list from X on.
|
|
|
|
N is a count of the rtx's to print. Positive values print from the specified
|
|
rtx_insn on. Negative values print a window around the rtx_insn.
|
|
EG: -5 prints 2 rtx_insn's on either side (in addition to the specified
|
|
rtx_insn). */
|
|
|
|
DEBUG_FUNCTION void
|
|
debug_rtx_list (const rtx_insn *x, int n)
|
|
{
|
|
int i,count;
|
|
const rtx_insn *insn;
|
|
|
|
count = n == 0 ? 1 : n < 0 ? -n : n;
|
|
|
|
/* If we are printing a window, back up to the start. */
|
|
|
|
if (n < 0)
|
|
for (i = count / 2; i > 0; i--)
|
|
{
|
|
if (PREV_INSN (x) == 0)
|
|
break;
|
|
x = PREV_INSN (x);
|
|
}
|
|
|
|
for (i = count, insn = x; i > 0 && insn != 0; i--, insn = NEXT_INSN (insn))
|
|
{
|
|
debug_rtx (insn);
|
|
fprintf (stderr, "\n");
|
|
}
|
|
}
|
|
|
|
/* Call this function to print an rtx_insn list from START to END
|
|
inclusive. */
|
|
|
|
DEBUG_FUNCTION void
|
|
debug_rtx_range (const rtx_insn *start, const rtx_insn *end)
|
|
{
|
|
while (1)
|
|
{
|
|
debug_rtx (start);
|
|
fprintf (stderr, "\n");
|
|
if (!start || start == end)
|
|
break;
|
|
start = NEXT_INSN (start);
|
|
}
|
|
}
|
|
|
|
/* Call this function to search an rtx_insn list to find one with insn uid UID,
|
|
and then call debug_rtx_list to print it, using DEBUG_RTX_COUNT.
|
|
The found insn is returned to enable further debugging analysis. */
|
|
|
|
DEBUG_FUNCTION const rtx_insn *
|
|
debug_rtx_find (const rtx_insn *x, int uid)
|
|
{
|
|
while (x != 0 && INSN_UID (x) != uid)
|
|
x = NEXT_INSN (x);
|
|
if (x != 0)
|
|
{
|
|
debug_rtx_list (x, debug_rtx_count);
|
|
return x;
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, "insn uid %d not found\n", uid);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* External entry point for printing a chain of insns
|
|
starting with RTX_FIRST.
|
|
A blank line separates insns.
|
|
|
|
If RTX_FIRST is not an insn, then it alone is printed, with no newline. */
|
|
|
|
void
|
|
rtx_writer::print_rtl (const_rtx rtx_first)
|
|
{
|
|
const rtx_insn *tmp_rtx;
|
|
|
|
if (rtx_first == 0)
|
|
{
|
|
fputs (print_rtx_head, m_outfile);
|
|
fputs ("(nil)\n", m_outfile);
|
|
}
|
|
else
|
|
switch (GET_CODE (rtx_first))
|
|
{
|
|
case INSN:
|
|
case JUMP_INSN:
|
|
case CALL_INSN:
|
|
case NOTE:
|
|
case CODE_LABEL:
|
|
case JUMP_TABLE_DATA:
|
|
case BARRIER:
|
|
for (tmp_rtx = as_a <const rtx_insn *> (rtx_first);
|
|
tmp_rtx != 0;
|
|
tmp_rtx = NEXT_INSN (tmp_rtx))
|
|
{
|
|
fputs (print_rtx_head, m_outfile);
|
|
print_rtx (tmp_rtx);
|
|
fprintf (m_outfile, "\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fputs (print_rtx_head, m_outfile);
|
|
print_rtx (rtx_first);
|
|
}
|
|
}
|
|
|
|
/* External entry point for printing a chain of insns
|
|
starting with RTX_FIRST onto file OUTF.
|
|
A blank line separates insns.
|
|
|
|
If RTX_FIRST is not an insn, then it alone is printed, with no newline. */
|
|
|
|
void
|
|
print_rtl (FILE *outf, const_rtx rtx_first)
|
|
{
|
|
rtx_writer w (outf, 0, false, false, NULL);
|
|
w.print_rtl (rtx_first);
|
|
}
|
|
|
|
/* Like print_rtx, except specify a file. */
|
|
|
|
void
|
|
print_rtl_single (FILE *outf, const_rtx x)
|
|
{
|
|
rtx_writer w (outf, 0, false, false, NULL);
|
|
w.print_rtl_single_with_indent (x, 0);
|
|
}
|
|
|
|
/* Like print_rtl_single, except specify an indentation. */
|
|
|
|
void
|
|
rtx_writer::print_rtl_single_with_indent (const_rtx x, int ind)
|
|
{
|
|
char *s_indent = (char *) alloca ((size_t) ind + 1);
|
|
memset ((void *) s_indent, ' ', (size_t) ind);
|
|
s_indent[ind] = '\0';
|
|
fputs (s_indent, m_outfile);
|
|
fputs (print_rtx_head, m_outfile);
|
|
|
|
int old_indent = m_indent;
|
|
m_indent = ind;
|
|
m_sawclose = false;
|
|
print_rtx (x);
|
|
putc ('\n', m_outfile);
|
|
m_indent = old_indent;
|
|
}
|
|
|
|
|
|
/* Like print_rtl except without all the detail; for example,
|
|
if RTX is a CONST_INT then print in decimal format. */
|
|
|
|
void
|
|
print_simple_rtl (FILE *outf, const_rtx x)
|
|
{
|
|
rtx_writer w (outf, 0, true, false, NULL);
|
|
w.print_rtl (x);
|
|
}
|
|
|
|
/* Print the elements of VEC to FILE. */
|
|
|
|
void
|
|
print_rtx_insn_vec (FILE *file, const vec<rtx_insn *> &vec)
|
|
{
|
|
fputc('{', file);
|
|
|
|
unsigned int len = vec.length ();
|
|
for (unsigned int i = 0; i < len; i++)
|
|
{
|
|
print_rtl_single (file, vec[i]);
|
|
if (i < len - 1)
|
|
fputs (", ", file);
|
|
}
|
|
|
|
fputc ('}', file);
|
|
}
|
|
|
|
#ifndef GENERATOR_FILE
|
|
/* The functions below try to print RTL in a form resembling assembler
|
|
mnemonics. Because this form is more concise than the "traditional" form
|
|
of RTL printing in Lisp-style, the form printed by this file is called
|
|
"slim". RTL dumps in slim format can be obtained by appending the "-slim"
|
|
option to -fdump-rtl-<pass>. Control flow graph output as a DOT file is
|
|
always printed in slim form.
|
|
|
|
The normal interface to the functionality provided in this pretty-printer
|
|
is through the dump_*_slim functions to print to a stream, or via the
|
|
print_*_slim functions to print into a user's pretty-printer.
|
|
|
|
It is also possible to obtain a string for a single pattern as a string
|
|
pointer, via str_pattern_slim, but this usage is discouraged. */
|
|
|
|
/* This recognizes rtx'en classified as expressions. These are always
|
|
represent some action on values or results of other expression, that
|
|
may be stored in objects representing values. */
|
|
|
|
static void
|
|
print_exp (pretty_printer *pp, const_rtx x, int verbose)
|
|
{
|
|
const char *st[4];
|
|
const char *fun;
|
|
rtx op[4];
|
|
int i;
|
|
|
|
fun = (char *) 0;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
st[i] = (char *) 0;
|
|
op[i] = NULL_RTX;
|
|
}
|
|
|
|
switch (GET_CODE (x))
|
|
{
|
|
case PLUS:
|
|
op[0] = XEXP (x, 0);
|
|
if (CONST_INT_P (XEXP (x, 1))
|
|
&& INTVAL (XEXP (x, 1)) < 0)
|
|
{
|
|
st[1] = "-";
|
|
op[1] = GEN_INT (-INTVAL (XEXP (x, 1)));
|
|
}
|
|
else
|
|
{
|
|
st[1] = "+";
|
|
op[1] = XEXP (x, 1);
|
|
}
|
|
break;
|
|
case LO_SUM:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "+low(";
|
|
op[1] = XEXP (x, 1);
|
|
st[2] = ")";
|
|
break;
|
|
case MINUS:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "-";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case COMPARE:
|
|
fun = "cmp";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case NEG:
|
|
st[0] = "-";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case FMA:
|
|
st[0] = "{";
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "*";
|
|
op[1] = XEXP (x, 1);
|
|
st[2] = "+";
|
|
op[2] = XEXP (x, 2);
|
|
st[3] = "}";
|
|
break;
|
|
case MULT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "*";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case DIV:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "/";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case UDIV:
|
|
fun = "udiv";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case MOD:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "%";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case UMOD:
|
|
fun = "umod";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case SMIN:
|
|
fun = "smin";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case SMAX:
|
|
fun = "smax";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case UMIN:
|
|
fun = "umin";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case UMAX:
|
|
fun = "umax";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case NOT:
|
|
st[0] = "~";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case AND:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "&";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case IOR:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "|";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case XOR:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "^";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case ASHIFT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "<<";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case LSHIFTRT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = " 0>>";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case ASHIFTRT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = ">>";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case ROTATE:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "<-<";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case ROTATERT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = ">->";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case NE:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "!=";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case EQ:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "==";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case GE:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = ">=";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case GT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = ">";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case LE:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "<=";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case LT:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "<";
|
|
op[1] = XEXP (x, 1);
|
|
break;
|
|
case SIGN_EXTRACT:
|
|
fun = (verbose) ? "sign_extract" : "sxt";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
op[2] = XEXP (x, 2);
|
|
break;
|
|
case ZERO_EXTRACT:
|
|
fun = (verbose) ? "zero_extract" : "zxt";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
op[2] = XEXP (x, 2);
|
|
break;
|
|
case SIGN_EXTEND:
|
|
fun = (verbose) ? "sign_extend" : "sxn";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case ZERO_EXTEND:
|
|
fun = (verbose) ? "zero_extend" : "zxn";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case FLOAT_EXTEND:
|
|
fun = (verbose) ? "float_extend" : "fxn";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case TRUNCATE:
|
|
fun = (verbose) ? "trunc" : "trn";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case FLOAT_TRUNCATE:
|
|
fun = (verbose) ? "float_trunc" : "ftr";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case FLOAT:
|
|
fun = (verbose) ? "float" : "flt";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case UNSIGNED_FLOAT:
|
|
fun = (verbose) ? "uns_float" : "ufl";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case FIX:
|
|
fun = "fix";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case UNSIGNED_FIX:
|
|
fun = (verbose) ? "uns_fix" : "ufx";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case PRE_DEC:
|
|
st[0] = "--";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case PRE_INC:
|
|
st[0] = "++";
|
|
op[0] = XEXP (x, 0);
|
|
break;
|
|
case POST_DEC:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "--";
|
|
break;
|
|
case POST_INC:
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = "++";
|
|
break;
|
|
case PRE_MODIFY:
|
|
st[0] = "pre ";
|
|
op[0] = XEXP (XEXP (x, 1), 0);
|
|
st[1] = "+=";
|
|
op[1] = XEXP (XEXP (x, 1), 1);
|
|
break;
|
|
case POST_MODIFY:
|
|
st[0] = "post ";
|
|
op[0] = XEXP (XEXP (x, 1), 0);
|
|
st[1] = "+=";
|
|
op[1] = XEXP (XEXP (x, 1), 1);
|
|
break;
|
|
case CALL:
|
|
st[0] = "call ";
|
|
op[0] = XEXP (x, 0);
|
|
if (verbose)
|
|
{
|
|
st[1] = " argc:";
|
|
op[1] = XEXP (x, 1);
|
|
}
|
|
break;
|
|
case IF_THEN_ELSE:
|
|
st[0] = "{(";
|
|
op[0] = XEXP (x, 0);
|
|
st[1] = ")?";
|
|
op[1] = XEXP (x, 1);
|
|
st[2] = ":";
|
|
op[2] = XEXP (x, 2);
|
|
st[3] = "}";
|
|
break;
|
|
case TRAP_IF:
|
|
fun = "trap_if";
|
|
op[0] = TRAP_CONDITION (x);
|
|
break;
|
|
case PREFETCH:
|
|
fun = "prefetch";
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
op[2] = XEXP (x, 2);
|
|
break;
|
|
case UNSPEC:
|
|
case UNSPEC_VOLATILE:
|
|
{
|
|
pp_string (pp, "unspec");
|
|
if (GET_CODE (x) == UNSPEC_VOLATILE)
|
|
pp_string (pp, "/v");
|
|
pp_left_bracket (pp);
|
|
for (i = 0; i < XVECLEN (x, 0); i++)
|
|
{
|
|
if (i != 0)
|
|
pp_comma (pp);
|
|
print_pattern (pp, XVECEXP (x, 0, i), verbose);
|
|
}
|
|
pp_string (pp, "] ");
|
|
pp_decimal_int (pp, XINT (x, 1));
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
/* Most unhandled codes can be printed as pseudo-functions. */
|
|
if (GET_RTX_CLASS (GET_CODE (x)) == RTX_UNARY)
|
|
{
|
|
fun = GET_RTX_NAME (GET_CODE (x));
|
|
op[0] = XEXP (x, 0);
|
|
}
|
|
else if (GET_RTX_CLASS (GET_CODE (x)) == RTX_COMPARE
|
|
|| GET_RTX_CLASS (GET_CODE (x)) == RTX_COMM_COMPARE
|
|
|| GET_RTX_CLASS (GET_CODE (x)) == RTX_BIN_ARITH
|
|
|| GET_RTX_CLASS (GET_CODE (x)) == RTX_COMM_ARITH)
|
|
{
|
|
fun = GET_RTX_NAME (GET_CODE (x));
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
}
|
|
else if (GET_RTX_CLASS (GET_CODE (x)) == RTX_TERNARY)
|
|
{
|
|
fun = GET_RTX_NAME (GET_CODE (x));
|
|
op[0] = XEXP (x, 0);
|
|
op[1] = XEXP (x, 1);
|
|
op[2] = XEXP (x, 2);
|
|
}
|
|
else
|
|
/* Give up, just print the RTX name. */
|
|
st[0] = GET_RTX_NAME (GET_CODE (x));
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Print this as a function? */
|
|
if (fun)
|
|
{
|
|
pp_string (pp, fun);
|
|
pp_left_paren (pp);
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (st[i])
|
|
pp_string (pp, st[i]);
|
|
|
|
if (op[i])
|
|
{
|
|
if (fun && i != 0)
|
|
pp_comma (pp);
|
|
print_value (pp, op[i], verbose);
|
|
}
|
|
}
|
|
|
|
if (fun)
|
|
pp_right_paren (pp);
|
|
} /* print_exp */
|
|
|
|
/* Prints rtxes, I customarily classified as values. They're constants,
|
|
registers, labels, symbols and memory accesses. */
|
|
|
|
void
|
|
print_value (pretty_printer *pp, const_rtx x, int verbose)
|
|
{
|
|
char tmp[1024];
|
|
|
|
if (!x)
|
|
{
|
|
pp_string (pp, "(nil)");
|
|
return;
|
|
}
|
|
switch (GET_CODE (x))
|
|
{
|
|
case CONST_INT:
|
|
pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX,
|
|
(unsigned HOST_WIDE_INT) INTVAL (x));
|
|
break;
|
|
|
|
case CONST_WIDE_INT:
|
|
{
|
|
const char *sep = "<";
|
|
int i;
|
|
for (i = CONST_WIDE_INT_NUNITS (x) - 1; i >= 0; i--)
|
|
{
|
|
pp_string (pp, sep);
|
|
sep = ",";
|
|
sprintf (tmp, HOST_WIDE_INT_PRINT_HEX,
|
|
(unsigned HOST_WIDE_INT) CONST_WIDE_INT_ELT (x, i));
|
|
pp_string (pp, tmp);
|
|
}
|
|
pp_greater (pp);
|
|
}
|
|
break;
|
|
|
|
case CONST_POLY_INT:
|
|
pp_left_bracket (pp);
|
|
pp_wide_int (pp, CONST_POLY_INT_COEFFS (x)[0], SIGNED);
|
|
for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
|
|
{
|
|
pp_string (pp, ", ");
|
|
pp_wide_int (pp, CONST_POLY_INT_COEFFS (x)[i], SIGNED);
|
|
}
|
|
pp_right_bracket (pp);
|
|
break;
|
|
|
|
case CONST_DOUBLE:
|
|
if (FLOAT_MODE_P (GET_MODE (x)))
|
|
{
|
|
real_to_decimal (tmp, CONST_DOUBLE_REAL_VALUE (x),
|
|
sizeof (tmp), 0, 1);
|
|
pp_string (pp, tmp);
|
|
}
|
|
else
|
|
pp_printf (pp, "<%wx,%wx>",
|
|
(unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (x),
|
|
(unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (x));
|
|
break;
|
|
case CONST_FIXED:
|
|
fixed_to_decimal (tmp, CONST_FIXED_VALUE (x), sizeof (tmp));
|
|
pp_string (pp, tmp);
|
|
break;
|
|
case CONST_STRING:
|
|
pp_string (pp, "\"");
|
|
pretty_print_string (pp, XSTR (x, 0), strlen (XSTR (x, 0)));
|
|
pp_string (pp, "\"");
|
|
break;
|
|
case SYMBOL_REF:
|
|
pp_printf (pp, "`%s'", XSTR (x, 0));
|
|
break;
|
|
case LABEL_REF:
|
|
pp_printf (pp, "L%d", INSN_UID (label_ref_label (x)));
|
|
break;
|
|
case CONST:
|
|
case HIGH:
|
|
case STRICT_LOW_PART:
|
|
pp_printf (pp, "%s(", GET_RTX_NAME (GET_CODE (x)));
|
|
print_value (pp, XEXP (x, 0), verbose);
|
|
pp_right_paren (pp);
|
|
break;
|
|
case REG:
|
|
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
|
|
{
|
|
if (ISDIGIT (reg_names[REGNO (x)][0]))
|
|
pp_modulo (pp);
|
|
pp_string (pp, reg_names[REGNO (x)]);
|
|
}
|
|
else
|
|
pp_printf (pp, "r%d", REGNO (x));
|
|
if (verbose)
|
|
pp_printf (pp, ":%s", GET_MODE_NAME (GET_MODE (x)));
|
|
break;
|
|
case SUBREG:
|
|
print_value (pp, SUBREG_REG (x), verbose);
|
|
pp_printf (pp, "#");
|
|
pp_wide_integer (pp, SUBREG_BYTE (x));
|
|
break;
|
|
case SCRATCH:
|
|
case PC:
|
|
pp_string (pp, GET_RTX_NAME (GET_CODE (x)));
|
|
break;
|
|
case MEM:
|
|
pp_left_bracket (pp);
|
|
print_value (pp, XEXP (x, 0), verbose);
|
|
pp_right_bracket (pp);
|
|
break;
|
|
case DEBUG_EXPR:
|
|
pp_printf (pp, "D#%i", DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x)));
|
|
break;
|
|
default:
|
|
print_exp (pp, x, verbose);
|
|
break;
|
|
}
|
|
} /* print_value */
|
|
|
|
/* The next step in insn detalization, its pattern recognition. */
|
|
|
|
void
|
|
print_pattern (pretty_printer *pp, const_rtx x, int verbose)
|
|
{
|
|
if (! x)
|
|
{
|
|
pp_string (pp, "(nil)");
|
|
return;
|
|
}
|
|
|
|
switch (GET_CODE (x))
|
|
{
|
|
case SET:
|
|
print_value (pp, SET_DEST (x), verbose);
|
|
pp_equal (pp);
|
|
print_value (pp, SET_SRC (x), verbose);
|
|
break;
|
|
case RETURN:
|
|
case SIMPLE_RETURN:
|
|
case EH_RETURN:
|
|
pp_string (pp, GET_RTX_NAME (GET_CODE (x)));
|
|
break;
|
|
case CALL:
|
|
print_exp (pp, x, verbose);
|
|
break;
|
|
case CLOBBER:
|
|
case USE:
|
|
pp_printf (pp, "%s ", GET_RTX_NAME (GET_CODE (x)));
|
|
print_value (pp, XEXP (x, 0), verbose);
|
|
break;
|
|
case VAR_LOCATION:
|
|
pp_string (pp, "loc ");
|
|
print_value (pp, PAT_VAR_LOCATION_LOC (x), verbose);
|
|
break;
|
|
case COND_EXEC:
|
|
pp_left_paren (pp);
|
|
if (GET_CODE (COND_EXEC_TEST (x)) == NE
|
|
&& XEXP (COND_EXEC_TEST (x), 1) == const0_rtx)
|
|
print_value (pp, XEXP (COND_EXEC_TEST (x), 0), verbose);
|
|
else if (GET_CODE (COND_EXEC_TEST (x)) == EQ
|
|
&& XEXP (COND_EXEC_TEST (x), 1) == const0_rtx)
|
|
{
|
|
pp_exclamation (pp);
|
|
print_value (pp, XEXP (COND_EXEC_TEST (x), 0), verbose);
|
|
}
|
|
else
|
|
print_value (pp, COND_EXEC_TEST (x), verbose);
|
|
pp_string (pp, ") ");
|
|
print_pattern (pp, COND_EXEC_CODE (x), verbose);
|
|
break;
|
|
case PARALLEL:
|
|
{
|
|
int i;
|
|
|
|
pp_left_brace (pp);
|
|
for (i = 0; i < XVECLEN (x, 0); i++)
|
|
{
|
|
print_pattern (pp, XVECEXP (x, 0, i), verbose);
|
|
pp_semicolon (pp);
|
|
}
|
|
pp_right_brace (pp);
|
|
}
|
|
break;
|
|
case SEQUENCE:
|
|
{
|
|
const rtx_sequence *seq = as_a <const rtx_sequence *> (x);
|
|
pp_string (pp, "sequence{");
|
|
if (INSN_P (seq->element (0)))
|
|
{
|
|
/* Print the sequence insns indented. */
|
|
const char * save_print_rtx_head = print_rtx_head;
|
|
char indented_print_rtx_head[32];
|
|
|
|
pp_newline (pp);
|
|
gcc_assert (strlen (print_rtx_head) < sizeof (indented_print_rtx_head) - 4);
|
|
snprintf (indented_print_rtx_head,
|
|
sizeof (indented_print_rtx_head),
|
|
"%s ", print_rtx_head);
|
|
print_rtx_head = indented_print_rtx_head;
|
|
for (int i = 0; i < seq->len (); i++)
|
|
print_insn_with_notes (pp, seq->insn (i));
|
|
pp_printf (pp, "%s ", save_print_rtx_head);
|
|
print_rtx_head = save_print_rtx_head;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < seq->len (); i++)
|
|
{
|
|
print_pattern (pp, seq->element (i), verbose);
|
|
pp_semicolon (pp);
|
|
}
|
|
}
|
|
pp_right_brace (pp);
|
|
}
|
|
break;
|
|
case ASM_INPUT:
|
|
pp_printf (pp, "asm {%s}", XSTR (x, 0));
|
|
break;
|
|
case ADDR_VEC:
|
|
for (int i = 0; i < XVECLEN (x, 0); i++)
|
|
{
|
|
print_value (pp, XVECEXP (x, 0, i), verbose);
|
|
pp_semicolon (pp);
|
|
}
|
|
break;
|
|
case ADDR_DIFF_VEC:
|
|
for (int i = 0; i < XVECLEN (x, 1); i++)
|
|
{
|
|
print_value (pp, XVECEXP (x, 1, i), verbose);
|
|
pp_semicolon (pp);
|
|
}
|
|
break;
|
|
case TRAP_IF:
|
|
pp_string (pp, "trap_if ");
|
|
print_value (pp, TRAP_CONDITION (x), verbose);
|
|
break;
|
|
case UNSPEC:
|
|
case UNSPEC_VOLATILE:
|
|
/* Fallthru -- leave UNSPECs to print_exp. */
|
|
default:
|
|
print_value (pp, x, verbose);
|
|
}
|
|
} /* print_pattern */
|
|
|
|
/* This is the main function in slim rtl visualization mechanism.
|
|
|
|
X is an insn, to be printed into PP.
|
|
|
|
This function tries to print it properly in human-readable form,
|
|
resembling assembler mnemonics (instead of the older Lisp-style
|
|
form).
|
|
|
|
If VERBOSE is TRUE, insns are printed with more complete (but
|
|
longer) pattern names and with extra information, and prefixed
|
|
with their INSN_UIDs. */
|
|
|
|
void
|
|
print_insn (pretty_printer *pp, const rtx_insn *x, int verbose)
|
|
{
|
|
if (verbose)
|
|
{
|
|
/* Blech, pretty-print can't print integers with a specified width. */
|
|
char uid_prefix[32];
|
|
snprintf (uid_prefix, sizeof uid_prefix, " %4d: ", INSN_UID (x));
|
|
pp_string (pp, uid_prefix);
|
|
}
|
|
|
|
switch (GET_CODE (x))
|
|
{
|
|
case INSN:
|
|
print_pattern (pp, PATTERN (x), verbose);
|
|
break;
|
|
|
|
case DEBUG_INSN:
|
|
{
|
|
if (DEBUG_MARKER_INSN_P (x))
|
|
{
|
|
switch (INSN_DEBUG_MARKER_KIND (x))
|
|
{
|
|
case NOTE_INSN_BEGIN_STMT:
|
|
pp_string (pp, "debug begin stmt marker");
|
|
break;
|
|
|
|
case NOTE_INSN_INLINE_ENTRY:
|
|
pp_string (pp, "debug inline entry marker");
|
|
break;
|
|
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
break;
|
|
}
|
|
|
|
const char *name = "?";
|
|
char idbuf[32];
|
|
|
|
if (DECL_P (INSN_VAR_LOCATION_DECL (x)))
|
|
{
|
|
tree id = DECL_NAME (INSN_VAR_LOCATION_DECL (x));
|
|
if (id)
|
|
name = IDENTIFIER_POINTER (id);
|
|
else if (TREE_CODE (INSN_VAR_LOCATION_DECL (x))
|
|
== DEBUG_EXPR_DECL)
|
|
{
|
|
sprintf (idbuf, "D#%i",
|
|
DEBUG_TEMP_UID (INSN_VAR_LOCATION_DECL (x)));
|
|
name = idbuf;
|
|
}
|
|
else
|
|
{
|
|
sprintf (idbuf, "D.%i",
|
|
DECL_UID (INSN_VAR_LOCATION_DECL (x)));
|
|
name = idbuf;
|
|
}
|
|
}
|
|
pp_printf (pp, "debug %s => ", name);
|
|
if (VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (x)))
|
|
pp_string (pp, "optimized away");
|
|
else
|
|
print_pattern (pp, INSN_VAR_LOCATION_LOC (x), verbose);
|
|
}
|
|
break;
|
|
|
|
case JUMP_INSN:
|
|
print_pattern (pp, PATTERN (x), verbose);
|
|
break;
|
|
case CALL_INSN:
|
|
if (GET_CODE (PATTERN (x)) == PARALLEL)
|
|
print_pattern (pp, XVECEXP (PATTERN (x), 0, 0), verbose);
|
|
else
|
|
print_pattern (pp, PATTERN (x), verbose);
|
|
break;
|
|
case CODE_LABEL:
|
|
pp_printf (pp, "L%d:", INSN_UID (x));
|
|
break;
|
|
case JUMP_TABLE_DATA:
|
|
pp_string (pp, "jump_table_data{\n");
|
|
print_pattern (pp, PATTERN (x), verbose);
|
|
pp_right_brace (pp);
|
|
break;
|
|
case BARRIER:
|
|
pp_string (pp, "barrier");
|
|
break;
|
|
case NOTE:
|
|
{
|
|
pp_string (pp, GET_NOTE_INSN_NAME (NOTE_KIND (x)));
|
|
switch (NOTE_KIND (x))
|
|
{
|
|
case NOTE_INSN_EH_REGION_BEG:
|
|
case NOTE_INSN_EH_REGION_END:
|
|
pp_printf (pp, " %d", NOTE_EH_HANDLER (x));
|
|
break;
|
|
|
|
case NOTE_INSN_BLOCK_BEG:
|
|
case NOTE_INSN_BLOCK_END:
|
|
pp_printf (pp, " %d", BLOCK_NUMBER (NOTE_BLOCK (x)));
|
|
break;
|
|
|
|
case NOTE_INSN_BASIC_BLOCK:
|
|
pp_printf (pp, " %d", NOTE_BASIC_BLOCK (x)->index);
|
|
break;
|
|
|
|
case NOTE_INSN_DELETED_LABEL:
|
|
case NOTE_INSN_DELETED_DEBUG_LABEL:
|
|
{
|
|
const char *label = NOTE_DELETED_LABEL_NAME (x);
|
|
if (label == NULL)
|
|
label = "";
|
|
pp_printf (pp, " (\"%s\")", label);
|
|
}
|
|
break;
|
|
|
|
case NOTE_INSN_VAR_LOCATION:
|
|
pp_left_brace (pp);
|
|
print_pattern (pp, NOTE_VAR_LOCATION (x), verbose);
|
|
pp_right_brace (pp);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
} /* print_insn */
|
|
|
|
/* Pretty-print a slim dump of X (an insn) to PP, including any register
|
|
note attached to the instruction. */
|
|
|
|
void
|
|
print_insn_with_notes (pretty_printer *pp, const rtx_insn *x)
|
|
{
|
|
pp_string (pp, print_rtx_head);
|
|
print_insn (pp, x, 1);
|
|
pp_newline (pp);
|
|
if (INSN_P (x) && REG_NOTES (x))
|
|
for (rtx note = REG_NOTES (x); note; note = XEXP (note, 1))
|
|
{
|
|
pp_printf (pp, "%s %s ", print_rtx_head,
|
|
GET_REG_NOTE_NAME (REG_NOTE_KIND (note)));
|
|
if (GET_CODE (note) == INT_LIST)
|
|
pp_printf (pp, "%d", XINT (note, 0));
|
|
else
|
|
print_pattern (pp, XEXP (note, 0), 1);
|
|
pp_newline (pp);
|
|
}
|
|
}
|
|
|
|
/* Print X, an RTL value node, to file F in slim format. Include
|
|
additional information if VERBOSE is nonzero.
|
|
|
|
Value nodes are constants, registers, labels, symbols and
|
|
memory. */
|
|
|
|
void
|
|
dump_value_slim (FILE *f, const_rtx x, int verbose)
|
|
{
|
|
pretty_printer rtl_slim_pp;
|
|
rtl_slim_pp.set_output_stream (f);
|
|
print_value (&rtl_slim_pp, x, verbose);
|
|
pp_flush (&rtl_slim_pp);
|
|
}
|
|
|
|
/* Emit a slim dump of X (an insn) to the file F, including any register
|
|
note attached to the instruction. */
|
|
void
|
|
dump_insn_slim (FILE *f, const rtx_insn *x)
|
|
{
|
|
pretty_printer rtl_slim_pp;
|
|
rtl_slim_pp.set_output_stream (f);
|
|
print_insn_with_notes (&rtl_slim_pp, x);
|
|
pp_flush (&rtl_slim_pp);
|
|
}
|
|
|
|
/* Same as above, but stop at LAST or when COUNT == 0.
|
|
If COUNT < 0 it will stop only at LAST or NULL rtx. */
|
|
|
|
void
|
|
dump_rtl_slim (FILE *f, const rtx_insn *first, const rtx_insn *last,
|
|
int count, int flags ATTRIBUTE_UNUSED)
|
|
{
|
|
const rtx_insn *insn, *tail;
|
|
pretty_printer rtl_slim_pp;
|
|
rtl_slim_pp.set_output_stream (f);
|
|
|
|
tail = last ? NEXT_INSN (last) : NULL;
|
|
for (insn = first;
|
|
(insn != NULL) && (insn != tail) && (count != 0);
|
|
insn = NEXT_INSN (insn))
|
|
{
|
|
print_insn_with_notes (&rtl_slim_pp, insn);
|
|
if (count > 0)
|
|
count--;
|
|
}
|
|
|
|
pp_flush (&rtl_slim_pp);
|
|
}
|
|
|
|
/* Dumps basic block BB to pretty-printer PP in slim form and without and
|
|
no indentation, for use as a label of a DOT graph record-node. */
|
|
|
|
void
|
|
rtl_dump_bb_for_graph (pretty_printer *pp, basic_block bb)
|
|
{
|
|
rtx_insn *insn;
|
|
bool first = true;
|
|
|
|
/* TODO: inter-bb stuff. */
|
|
FOR_BB_INSNS (bb, insn)
|
|
{
|
|
if (! first)
|
|
{
|
|
pp_bar (pp);
|
|
pp_write_text_to_stream (pp);
|
|
}
|
|
first = false;
|
|
print_insn_with_notes (pp, insn);
|
|
pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
rtl_dump_bb_as_sarif_properties (diagnostics::sarif_builder *,
|
|
json::object &output_bag,
|
|
basic_block bb)
|
|
{
|
|
/* TODO: inter-bb stuff. */
|
|
auto json_insn_arr = std::make_unique<json::array> ();
|
|
rtx_insn *insn;
|
|
FOR_BB_INSNS (bb, insn)
|
|
{
|
|
pretty_printer pp;
|
|
print_insn_with_notes (&pp, insn);
|
|
json_insn_arr->append_string (pp_formatted_text (&pp));
|
|
}
|
|
output_bag.set_array_of_string
|
|
(custom_sarif_properties::cfg::basic_block::rtl::insns,
|
|
std::move (json_insn_arr));
|
|
}
|
|
|
|
/* Pretty-print pattern X of some insn in non-verbose mode.
|
|
Return a string pointer to the pretty-printer buffer.
|
|
|
|
This function is only exported exists only to accommodate some older users
|
|
of the slim RTL pretty printers. Please do not use it for new code. */
|
|
|
|
const char *
|
|
str_pattern_slim (const_rtx x)
|
|
{
|
|
pretty_printer rtl_slim_pp;
|
|
print_pattern (&rtl_slim_pp, x, 0);
|
|
return ggc_strdup (pp_formatted_text (&rtl_slim_pp));
|
|
}
|
|
|
|
/* Emit a slim dump of X (an insn) to stderr. */
|
|
extern void debug_insn_slim (const rtx_insn *);
|
|
DEBUG_FUNCTION void
|
|
debug_insn_slim (const rtx_insn *x)
|
|
{
|
|
dump_insn_slim (stderr, x);
|
|
}
|
|
|
|
/* Same as above, but using dump_rtl_slim. */
|
|
extern void debug_rtl_slim (FILE *, const rtx_insn *, const rtx_insn *,
|
|
int, int);
|
|
DEBUG_FUNCTION void
|
|
debug_rtl_slim (const rtx_insn *first, const rtx_insn *last, int count,
|
|
int flags)
|
|
{
|
|
dump_rtl_slim (stderr, first, last, count, flags);
|
|
}
|
|
|
|
extern void debug_bb_slim (basic_block);
|
|
DEBUG_FUNCTION void
|
|
debug_bb_slim (basic_block bb)
|
|
{
|
|
debug_bb (bb, TDF_SLIM | TDF_BLOCKS);
|
|
}
|
|
|
|
extern void debug_bb_n_slim (int);
|
|
DEBUG_FUNCTION void
|
|
debug_bb_n_slim (int n)
|
|
{
|
|
basic_block bb = BASIC_BLOCK_FOR_FN (cfun, n);
|
|
debug_bb_slim (bb);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if __GNUC__ >= 10
|
|
# pragma GCC diagnostic pop
|
|
#endif
|