From a5d9debedd2f46a699c7f0a206f4e0b7d620a111 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 11 Jul 2025 14:58:21 -0400 Subject: [PATCH] diagnostics: add support for directed graphs; use them for state graphs MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit In r16-1631-g2334d30cd8feac I added support for capturing state information from -fanalyzer in XML form, and adding a way to visualize these states in HTML output. The data was optionally captured in SARIF output (with "xml-state=yes"), stashing the XML in string form in a property bag. This worked, but there was no way to round-trip the stored data back from SARIF without adding an XML parser to GCC, which I don't want to do. SARIF supports capturing directed graphs, so this patch: (a) adds a new namespace diagnostics::digraphs, with classes digraph, node, and edge, representing directed graphs in a form similar to what SARIF can serialize (b) adds support to GCC's diagnostic subsystem for reporting graphs, either "globally" or as part of a diagnostic. An example in a testsuite plugin emits an error that has a couple of dummy graphs associated with it, and captures the optimization passes as a digraph "globally". Graphs are ignored by text sinks, but are captured by sarif sinks, and the "experimental-html" sink gains SVG-based rendering of any graphs using dot. This HTML output is rather crude; an example can be seen here: https://dmalcolm.fedorapeople.org/gcc/2025-07-10/diagnostic-test-graphs-html.c.html (c) adds support to libgdiagnostics for the above (d) adds support to sarif-replay for the above (round-tripping any graph information) (e) replaces the XML representation of state with a representation based on the above directed graphs, using property bags to stash additional information (e.g. "this is an on-stack buffer") (f) implements round-tripping of this information in sarif-replay To summarize: - previously we could generate HTML diagrams for debugging -fanalyzer directly from gcc, but not from stored .sarif output. - with this patch, we can generate such HTML diagrams both directly *and* from stored .sarif output (provided the SARIF sink was created with "state-graphs=yes") Examples of HTML output can be seen here: https://dmalcolm.fedorapeople.org/gcc/2025-07-10/ where as before j/k can be used to cycle through the events. which is almost identical to the output from the old XML-based implementation seen at: https://dmalcolm.fedorapeople.org/gcc/2025-06-23/ gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add diagnostic-digraphs.o and diagnostic-state-graphs.o. gcc/ChangeLog: * diagnostic-format-html.cc: Include "diagnostic-format-sarif.h", Replace include of "diagnostic-state.h" with includes of "diagnostic-digraphs.h" and "diagnostic-state-graphs.h". (html_generation_options::html_generation_options): Update for field renaming. (html_builder::m_body_element): New field. (html_builder::html_builder): Initialize m_body_element. (html_builder::maybe_make_state_diagram): Port from XML implementation to state graph implementation. (html_builder::make_element_for_diagnostic): Add any per-diagnostic graphs. (html_builder::add_graph): New. (html_builder::emit_global_graph): New. (html_output_format::report_global_digraph): New. * diagnostic-format-html.h (html_generation_options::m_show_state_diagram_xml): Replace with... (html_generation_options::m_show_state_diagrams_sarif): ...this. (html_generation_options::m_show_state_diagram_dot_src): Rename to... (html_generation_options::m_show_state_diagrams_dot_src): ...this. * diagnostic-format-sarif.cc: Include "diagnostic-digraphs.h" and "diagnostic-state-graphs.h". (sarif_builder::m_run_graphs): New field. (sarif_result::on_nested_diagnostic): Update call to make_location_object to pass arg by pointer. (sarif_builder::sarif_builder): Initialize m_run_graphs. (sarif_builder::report_global_digraph): New. (sarif_builder::make_result_object): Add any graphs to the result object. (sarif_builder::make_locations_arr): Update call to make_location_object to pass arg by pointer. (sarif_builder::make_location_object): Pass param "loc_mgr" by pointer rather than by reference so that it can be null, and handle this case. (copy_any_property_bag): New. (make_sarif_graph): New. (make_sarif_node): New. (make_sarif_edge): New. (sarif_property_bag::set_graph): New. (populate_thread_flow_location_object): Port from XML implementation to state graph implementation. (make_run_object): Store any graphs. (sarif_output_format::report_global_digraph): New. (sarif_generation_options::sarif_generation_options): Rename m_xml_state to m_state_graph. (selftest::test_make_location_object): Update for change to make_location_object. * diagnostic-format-sarif.h: (sarif_generation_options::m_xml_state): Replace with... (sarif_generation_options::m_state_graph): ...this. (class sarif_location_manager): Add forward decl. (diagnostics::digraphs::digraph): New forward decl. (diagnostics::digraphs::node): New forward decl. (diagnostics::digraphs::edge): New forward decl. (sarif_property_bag::set_graph): New decl. (class sarif_graph): New. (class sarif_node): New. (class sarif_edge): New. (make_sarif_graph): New decl. (make_sarif_node): New decl. (make_sarif_edge): New decl. * diagnostic-format-text.h (diagnostic_text_output_format::report_global_digraph): New. * diagnostic-format.h (diagnostic_output_format::report_global_digraph): New vfunc. * diagnostic-digraphs.cc: New file. * diagnostic-digraphs.h: New file. * diagnostic-metadata.h (diagnostics::digraphs::lazy_digraphs): New forward decl. (diagnostic_metadata::diagnostic_metadata): Initialize m_lazy_digraphs. (diagnostic_metadata::set_lazy_digraphs): New. (diagnostic_metadata::get_lazy_digraphs): New. (diagnostic_metadata::m_lazy_digraphs): New field. * diagnostic-output-spec.cc (sarif_scheme_handler::make_sink): Update for XML to state graph changes. (sarif_scheme_handler::make_sarif_gen_opts): Likewise. (html_scheme_handler::make_sink): Rename "show-state-diagram-xml" to "show-state-diagrams-sarif" and use pluralization consistently. * diagnostic-path.cc: Replace include of "xml.h" with "diagnostic-state-graphs.h". (diagnostic_event::maybe_make_xml_state): Replace with... (diagnostic_event::maybe_make_diagnostic_state_graph): ...this. * diagnostic-path.h (diagnostics::digraphs::digraph): New forward decl. (diagnostic_event::maybe_make_xml_state): Replace with... (diagnostic_event::maybe_make_diagnostic_state_graph): ...this. * diagnostic-state-graphs.cc: New file. * diagnostic-state-graphs.h: New file. * diagnostic-state-to-dot.cc: Port implementation from XML to state graphs. * diagnostic-state.h: Deleted file. * diagnostic.cc (diagnostic_context::report_global_digraph): New. * diagnostic.h (diagnostics::digraphs::lazy_digraph): New forward decl. (diagnostic_context::report_global_digraph): New decl. * doc/analyzer.texi (Debugging the Analyzer): Update to reflect change from XML to state graphs. * doc/invoke.texi ("sarif" diagnostics sink): Replace "xml-state" with "state-graphs". ("experimental-html" diagnostics sink): Replace "show-state-diagrams-xml" with "show-state-diagrams-sarif" * doc/libgdiagnostics/topics/compatibility.rst (LIBGDIAGNOSTICS_ABI_3): New. * doc/libgdiagnostics/topics/graphs.rst: New file. * doc/libgdiagnostics/topics/index.rst: Add graphs.rst. * graphviz.h (node_id::operator=): New. * json.h (json::value::dyn_cast_string): New. (json::object::get_num_keys): New accessor. (json::object::get_key): New accessor. (json::string::dyn_cast_string): New. * libgdiagnostics++.h (class libgdiagnostics::graph): New. (class libgdiagnostics::node): New. (class libgdiagnostics::edge): New. (class libgdiagnostics::diagnostic::take_graph): New. (class libgdiagnostics::manager::take_global_graph): New. (class libgdiagnostics::graph::set_description): New. (class libgdiagnostics::graph::get_node_by_id): New. (class libgdiagnostics::graph::get_edge_by_id): New. (class libgdiagnostics::graph::add_edge): New. (class libgdiagnostics::node::set_label): New. (class libgdiagnostics::node::set_location): New. (class libgdiagnostics::node::set_logical_location): New. * libgdiagnostics-private.h: New file. * libgdiagnostics.cc: Define INCLUDE_STRING. Include "diagnostic-digraphs.h", "diagnostic-state-graphs.h", and "libgdiagnostics-private.h". (struct diagnostic_graph): New. (struct diagnostic_node): New. (struct diagnostic_edge): New. (libgdiagnostics_path_event::libgdiagnostics_path_event): Add state_graph param. (libgdiagnostics_path_event::maybe_make_diagnostic_state_graph): New. (libgdiagnostics_path_event::m_state_graph): New field. (diagnostic_execution_path::add_event_va): Add state_graph param. (class prebuilt_digraphs): New. (diagnostic::diagnostic): Use m_graphs in m_metadata. (diagnostic::take_graph): New. (diagnostic::get_graphs): New accessor. (diagnostic::m_graphs): New field. (diagnostic_manager::take_global_graph): New. (diagnostic_execution_path_add_event): Update for new param to add_event_va. (diagnostic_execution_path_add_event_va): Likewise. (diagnostic_graph::add_node_with_id): New public entrypoint. (diagnostic_graph::add_edge_with_label): New public entrypoint. (diagnostic_manager_new_graph): New public entrypoint. (diagnostic_manager_take_global_graph): New public entrypoint. (diagnostic_take_graph): New public entrypoint. (diagnostic_graph_release): New public entrypoint. (diagnostic_graph_set_description): New public entrypoint. (diagnostic_graph_add_node): New public entrypoint. (diagnostic_graph_add_edge): New public entrypoint. (diagnostic_graph_get_node_by_id): New public entrypoint. (diagnostic_graph_get_edge_by_id): New public entrypoint. (diagnostic_node_set_location): New public entrypoint. (diagnostic_node_set_label): New public entrypoint. (diagnostic_node_set_logical_location): New public entrypoint. (private_diagnostic_execution_path_add_event_2): New private entrypoint. (private_diagnostic_graph_set_property_bag): New private entrypoint. (private_diagnostic_node_set_property_bag): New private entrypoint. (private_diagnostic_edge_set_property_bag): New private entrypoint. * libgdiagnostics.h (diagnostic_graph): New typedef. (diagnostic_node): New typedef. (diagnostic_edge): New typedef. (diagnostic_manager_new_graph): New decl. (diagnostic_manager_take_global_graph): New decl. (diagnostic_take_graph): New decl. (diagnostic_graph_release): New decl. (diagnostic_graph_set_description): New decl. (diagnostic_graph_add_node): New decl. (diagnostic_graph_add_edge): New decl. (diagnostic_graph_get_node_by_id): New decl. (diagnostic_graph_get_edge_by_id): New decl. (diagnostic_node_set_label): New decl. (diagnostic_node_set_location): New decl. (diagnostic_node_set_logical_location): New decl. * libgdiagnostics.map (LIBGDIAGNOSTICS_ABI_3): New. * libsarifreplay.cc: Include "libgdiagnostics-private.h". (id_map): New "using". (sarif_replayer::report_invalid_sarif): Update for change to report_problem params. (sarif_replayer::report_unhandled_sarif): Likewise. (sarif_replayer::report_note): New. (sarif_replayer::report_problem): Pass param "ref" by pointer rather than reference and handle it being null. (sarif_replayer::maybe_get_property_bag): New. (sarif_replayer::maybe_get_property_bag_value): New. (sarif_replayer::handle_run_obj): Handle run-level "graphs" as per §3.14.20. (sarif_replayer::handle_result_obj): Handle result-level "graphs" as per §3.27.19. (handle_thread_flow_location_object): Optionally handle graphs stored in property "gcc/diagnostic_event/state_graph" as state graphs. (sarif_replayer::handle_graph_object): New. (sarif_replayer::handle_node_object): New. (sarif_replayer::handle_edge_object): New. (sarif_replayer::get_graph_node_by_id_property): New. * selftest-run-tests.cc (selftest::run_tests): Call selftest::diagnostic_graph_cc_tests and selftest::diagnostic_state_graph_cc_tests. * selftest.h (selftest::diagnostic_graph_cc_tests): New decl. (selftest::diagnostic_state_graph_cc_tests): New decl. gcc/analyzer/ChangeLog: * ana-state-to-diagnostic-state.cc: Reimplement, replacing XML-based implementation with one based on state graphs. * ana-state-to-diagnostic-state.h: Likewise. * checker-event.cc: Replace include of "xml.h" with include of "diagnostic-state-graphs.h". (checker_event::maybe_make_xml_state): Replace with... (checker_event::maybe_make_diagnostic_state_graph): ...this. * checker-event.h: Add include of "diagnostic-digraphs.h". (checker_event::maybe_make_xml_state): Replace decl with... (checker_event::maybe_make_diagnostic_state_graph): ...this. * engine.cc (exploded_node::on_stmt_pre): Replace "_analyzer_dump_xml" with "__analyzer_dump_sarif". * program-state.cc: Replace include of "diagnostic-state.h" with "diagnostic-state-graphs.h". (program_state::dump_dot): Port from XML to state graphs. * program-state.h: Drop reduntant forward decl of xml::document. (program_state::make_xml): Replace decl with... (program_state::make_diagnostic_state_graph): ...this. (program_state::dump_xml_to_pp): Drop decl. (program_state::dump_xml_to_file): Drop decl. (program_state::dump_xml): Drop decl. (program_state::dump_dump_sarif): New decl. * sm-malloc.cc (get_dynalloc_state_for_state): New. (malloc_state_machine::add_state_to_xml): Replace with... (malloc_state_machine::add_state_to_state_graph): ...this. * sm.cc (state_machine::add_state_to_xml): Replace with... (state_machine::add_state_to_state_graph): ...this. (state_machine::add_global_state_to_xml): Replace with... (state_machine::add_global_state_to_state_graph): ...this. * sm.h (class xml_state): Drop forward decl. (class analyzer_state_graph): New forward decl. (state_machine::add_state_to_xml): Replace decl with... (state_machine::add_state_to_state_graph): ...this. (state_machine::add_global_state_to_xml): Replace decl with... (state_machine::add_global_state_to_state_graph): ...this. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/state-diagram-1-sarif.py (test_xml_state): Rename to... (test_state_graph): ...this. Port from XML to SARIF graphs. * gcc.dg/analyzer/state-diagram-1.c: Update sink option from "sarif:xml-state=yes" to "sarif:state-graphs=yes". * gcc.dg/analyzer/state-diagram-5-sarif.c: Likewise. * gcc.dg/analyzer/state-diagram-5-sarif.py: Drop import of ET. (test_nested_types_in_xml_state): Rename to... (test_nested_types_in_state_graph): ...this. Port from XML to SARIF graphs. * gcc.dg/plugin/diagnostic-test-graphs-html.c: New test. * gcc.dg/plugin/diagnostic-test-graphs-html.py: New test script. * gcc.dg/plugin/diagnostic-test-graphs-sarif.c: New test. * gcc.dg/plugin/diagnostic-test-graphs-sarif.py: New test script. * gcc.dg/plugin/diagnostic-test-graphs.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_graphs.cc: New test plugin. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above. * lib/sarif.py (get_xml_state): Delete. (get_state_graph): New. (def get_state_node_attr): New. (get_state_node_kind): New. (get_state_node_name): New. (get_state_node_type): New. (get_state_node_value): New. * sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif: New test. * sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif: New test. * sarif-replay.dg/2.1.0-valid/graphs-check-html.py: New test script. * sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py: New test script. * sarif-replay.dg/2.1.0-valid/graphs.sarif: New test. Signed-off-by: David Malcolm --- gcc/Makefile.in | 2 + gcc/analyzer/ana-state-to-diagnostic-state.cc | 659 ++--- gcc/analyzer/ana-state-to-diagnostic-state.h | 87 +- gcc/analyzer/checker-event.cc | 12 +- gcc/analyzer/checker-event.h | 7 +- gcc/analyzer/engine.cc | 4 +- gcc/analyzer/program-state.cc | 12 +- gcc/analyzer/program-state.h | 15 +- gcc/analyzer/sm-malloc.cc | 52 +- gcc/analyzer/sm.cc | 10 +- gcc/analyzer/sm.h | 12 +- gcc/diagnostic-digraphs.cc | 484 ++++ gcc/diagnostic-digraphs.h | 411 +++ gcc/diagnostic-format-html.cc | 91 +- gcc/diagnostic-format-html.h | 6 +- gcc/diagnostic-format-sarif.cc | 221 +- gcc/diagnostic-format-sarif.h | 49 +- gcc/diagnostic-format-text.h | 6 + gcc/diagnostic-format.h | 3 + gcc/diagnostic-metadata.h | 29 +- gcc/diagnostic-output-spec.cc | 34 +- gcc/diagnostic-path.cc | 10 +- gcc/diagnostic-path.h | 12 +- gcc/diagnostic-state-graphs.cc | 158 ++ gcc/diagnostic-state-graphs.h | 156 ++ gcc/diagnostic-state-to-dot.cc | 605 ++-- gcc/diagnostic-state.h | 37 - gcc/diagnostic.cc | 8 + gcc/diagnostic.h | 11 + gcc/doc/analyzer.texi | 12 +- gcc/doc/invoke.texi | 12 +- .../libgdiagnostics/topics/compatibility.rst | 31 + gcc/doc/libgdiagnostics/topics/graphs.rst | 197 ++ gcc/doc/libgdiagnostics/topics/index.rst | 1 + gcc/graphviz.h | 10 + gcc/json.h | 5 + gcc/libgdiagnostics++.h | 163 ++ gcc/libgdiagnostics-private.h | 62 + gcc/libgdiagnostics.cc | 340 ++- gcc/libgdiagnostics.h | 134 + gcc/libgdiagnostics.map | 23 + gcc/libsarifreplay.cc | 435 ++- gcc/selftest-run-tests.cc | 2 + gcc/selftest.h | 2 + .../gcc.dg/analyzer/state-diagram-1-sarif.py | 53 +- .../gcc.dg/analyzer/state-diagram-1.c | 2 +- .../gcc.dg/analyzer/state-diagram-5-sarif.c | 2 +- .../gcc.dg/analyzer/state-diagram-5-sarif.py | 86 +- .../plugin/diagnostic-test-graphs-html.c | 13 + .../plugin/diagnostic-test-graphs-html.py | 48 + .../plugin/diagnostic-test-graphs-sarif.c | 16 + .../plugin/diagnostic-test-graphs-sarif.py | 55 + .../gcc.dg/plugin/diagnostic-test-graphs.c | 8 + .../plugin/diagnostic_plugin_test_graphs.cc | 283 ++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 4 + gcc/testsuite/lib/sarif.py | 26 +- .../3.40.2-duplicate-node-id.sarif | 23 + .../3.41.4-unrecognized-node-id.sarif | 16 + .../2.1.0-valid/graphs-check-html.py | 46 + .../graphs-check-sarif-roundtrip.py | 55 + .../sarif-replay.dg/2.1.0-valid/graphs.sarif | 2445 +++++++++++++++++ 61 files changed, 6881 insertions(+), 932 deletions(-) create mode 100644 gcc/diagnostic-digraphs.cc create mode 100644 gcc/diagnostic-digraphs.h create mode 100644 gcc/diagnostic-state-graphs.cc create mode 100644 gcc/diagnostic-state-graphs.h delete mode 100644 gcc/diagnostic-state.h create mode 100644 gcc/doc/libgdiagnostics/topics/graphs.rst create mode 100644 gcc/libgdiagnostics-private.h create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif diff --git a/gcc/Makefile.in b/gcc/Makefile.in index d5ceccc424b2..bdb52925bfe9 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1856,11 +1856,13 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \ diagnostic-format-sarif.o \ diagnostic-format-text.o \ diagnostic-global-context.o \ + diagnostic-digraphs.o \ diagnostic-macro-unwinding.o \ diagnostic-output-spec.o \ diagnostic-path.o \ diagnostic-path-output.o \ diagnostic-show-locus.o \ + diagnostic-state-graphs.o \ diagnostic-state-to-dot.o \ edit-context.o \ graphviz.o pex.o \ diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc index b85a2f1b2428..27012595c8ae 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.cc +++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc @@ -1,4 +1,4 @@ -/* Converting ana::program_state to XML state documents. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm . @@ -23,8 +23,8 @@ along with GCC; see the file COPYING3. If not see #define INCLUDE_SET #include "analyzer/common.h" -#include "xml.h" -#include "xml-printer.h" +#include "diagnostic-state-graphs.h" +#include "diagnostic-format-sarif.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" @@ -39,57 +39,47 @@ along with GCC; see the file COPYING3. If not see namespace ana { +using namespace ::diagnostics::state_graphs; + static void -set_wi_attr (xml::element &e, +set_wi_attr (state_node_ref state_node, const char *attr_name, const wide_int_ref &w, signop sgn) { pretty_printer pp; pp_wide_int (&pp, w, sgn); - e.set_attr (attr_name, pp_formatted_text (&pp)); + state_node.set_attr (attr_name, pp_formatted_text (&pp)); } static void -set_type_attr (xml::element &e, const_tree type) +set_type_attr (state_node_ref state_node, const_tree type) { gcc_assert (type); pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%T", type); - e.set_attr ("type", pp_formatted_text (&pp)); + state_node.set_type (pp_formatted_text (&pp)); } static void -set_bits_attr (xml::element &e, +set_bits_attr (state_node_ref state_node, bit_range bits) { pretty_printer pp; bits.dump_to_pp (&pp); - e.set_attr ("bits", pp_formatted_text (&pp)); + state_node.set_attr ("bits", pp_formatted_text (&pp)); } -static void -set_region_id_attr (xml::element &e, - const region ®) -{ - e.set_attr ("region_id", std::to_string (reg.get_id ())); -} +// class analyzer_state_graph : public diagnostics::digraphs::digraph -// class xml_state : public xml::document - -xml_state::xml_state (const program_state &state, - const extrinsic_state &ext_state) -: xml::document (), - m_state (state), +analyzer_state_graph::analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state) +: m_state (state), m_ext_state (ext_state), m_mgr (*ext_state.get_engine ()->get_model_manager ()), - m_root (nullptr) + m_next_id (0) { - auto root = std::make_unique ("state-diagram", false); - m_root = root.get (); - add_child (std::move (root)); - /* Find pointers to heap-allocated regions, and record their types, so that we have a user-friendly way of showing the memory (by field, rather than by byte offset). */ @@ -117,14 +107,14 @@ xml_state::xml_state (const program_state &state, for (int i = state.m_region_model->get_stack_depth () - 1; i >= 0; --i) { const frame_region *reg = state.m_region_model->get_frame_at_index (i); - get_or_create_element (*reg); + get_or_create_state_node (*reg); } /* Create bound memory. */ for (auto iter : *state.m_region_model->get_store ()) { const bool create_all = false; // "true" for verbose, for debugging - create_elements_for_binding_cluster (*iter.second, create_all); + create_state_nodes_for_binding_cluster (*iter.second, create_all); } /* TODO: Constraints. */ @@ -137,52 +127,165 @@ xml_state::xml_state (const program_state &state, { auto &sm = ext_state.get_sm (i); for (const auto &iter : *smap) - sm.add_state_to_xml (*this, *iter.first, iter.second.m_state); + sm.add_state_to_state_graph (*this, *iter.first, iter.second.m_state); if (auto s = smap->get_global_state ()) - sm.add_global_state_to_xml (*this, s); + sm.add_global_state_to_state_graph (*this, s); } } + + /* Process pending edges. */ + while (m_pending_edges.size () > 0) + { + pending_edge item = m_pending_edges.back (); + m_pending_edges.pop_back (); + + /* Ensure we have a node for the dst region. This + could lead to additional pending edges. */ + auto dst_node = get_or_create_state_node (item.m_dst_reg); + add_edge (nullptr, item.m_src_node.m_node, dst_node.m_node); + } } -xml::element & -xml_state::get_or_create_element (const region ®) +state_node_ref +analyzer_state_graph::get_or_create_state_node (const region ®) { - auto existing = m_region_to_element_map.find (®); - if (existing != m_region_to_element_map.end ()) + auto existing = m_region_to_state_node_map.find (®); + if (existing != m_region_to_state_node_map.end ()) return *existing->second; - auto &e = create_and_add_element (reg); - m_region_to_element_map[®] = &e; - return e; + auto ref = create_and_add_state_node (reg); + m_region_to_state_node_map[®] = &ref.m_node; + return ref; } -xml::element& -xml_state::create_and_add_element (const region ®) +state_node_ref +analyzer_state_graph::create_and_add_state_node (const region ®) { - auto e = create_element (reg); - xml::element &result = *e; + auto node = create_state_node (reg); + + state_node_ref result = *node; if (auto parent_reg = reg.get_parent_region ()) + if (parent_reg->get_kind () != RK_ROOT) + { + auto parent_state_node = get_or_create_state_node (*parent_reg); + parent_state_node.m_node.add_child (std::move (node)); + return result; + } + add_node (std::move (node)); + return result; +} + +std::string +analyzer_state_graph::make_node_id (const char *prefix) +{ + return std::string (prefix) + "-" + std::to_string (m_next_id++); +} + +std::string +analyzer_state_graph::make_node_id (const region ®) +{ + const char *prefix = nullptr; + switch (reg.get_kind ()) { - auto parent_element = &get_or_create_element (*parent_reg); - parent_element->add_child (std::move (e)); + case RK_ROOT: + default: + gcc_unreachable (); + break; + + case RK_GLOBALS: + return "globals"; + case RK_CODE: + return "code"; + case RK_STACK: + return "stack"; + case RK_HEAP: + return "heap"; + + case RK_FRAME: + prefix = "frame-region"; + break; + case RK_FUNCTION: + prefix = "function-region"; + break; + case RK_LABEL: + prefix = "label-region"; + break; + case RK_THREAD_LOCAL: + prefix = "thread-local-region"; + break; + case RK_SYMBOLIC: + prefix = "symbolic-region"; + break; + case RK_DECL: + prefix = "decl-region"; + break; + case RK_FIELD: + prefix = "field-region"; + break; + case RK_ELEMENT: + prefix = "element-region"; + break; + case RK_OFFSET: + prefix = "offset-region"; + break; + case RK_SIZED: + prefix = "sized-region"; + break; + case RK_CAST: + prefix = "cast-region"; + break; + case RK_HEAP_ALLOCATED: + prefix = "heap-allocated-region"; + break; + case RK_ALLOCA: + prefix = "alloca-region"; + break; + case RK_STRING: + prefix = "string-region"; + break; + case RK_BIT_RANGE: + prefix = "bit-range-region"; + break; + case RK_VAR_ARG: + prefix = "var-arg-region"; + break; + case RK_ERRNO: + prefix = "errno-region"; + break; + case RK_PRIVATE: + prefix = "private-region"; + break; + case RK_UNKNOWN: + prefix = "unknown-region"; + break; } - else - m_root->add_child (std::move (e)); - return result; + return std::string (prefix) + "-" + std::to_string (reg.get_id ()); +} + +std::unique_ptr +analyzer_state_graph:: +make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id) +{ + auto node = std::make_unique (*this, std::move (id)); + state_node_ref node_ref (*node); + node_ref.set_node_kind (kind); + return node; } -std::unique_ptr -xml_state::make_memory_space_element (const char *label) +std::unique_ptr +analyzer_state_graph:: +make_memspace_state_node (const region ®, + diagnostics::state_graphs::node_kind kind) { - auto e = std::make_unique ("memory-space", false); - e->set_attr ("label", label); - return e; + return make_state_node (kind, make_node_id (reg)); } -std::unique_ptr -xml_state::create_element (const region ®) +std::unique_ptr +analyzer_state_graph::create_state_node (const region ®) { - std::unique_ptr e; + std::unique_ptr node; + switch (reg.get_kind ()) { default: @@ -190,110 +293,102 @@ xml_state::create_element (const region ®) case RK_FRAME: { - e = std::make_unique ("stack-frame", false); const frame_region &frame_reg = static_cast (reg); + + node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame, + make_node_id (reg)); + node->set_logical_loc + (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ())); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", frame_reg.get_fndecl ()); - e->set_attr ("function", pp_formatted_text (&pp)); + node->set_attr (STATE_NODE_PREFIX, "function", + pp_formatted_text (&pp)); } } break; + case RK_GLOBALS: - e = make_memory_space_element ("Globals"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::globals); break; case RK_CODE: - e = make_memory_space_element ("Code"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::code); break; case RK_FUNCTION: - e = std::make_unique ("function", false); - // TODO - break; - case RK_LABEL: - e = std::make_unique ("label", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::function); // TODO break; + case RK_STACK: - e = std::make_unique ("stack", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::stack); break; case RK_HEAP: - e = make_memory_space_element ("Heap"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::heap_); break; case RK_THREAD_LOCAL: - e = make_memory_space_element ("Thread-local"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::thread_local_); break; case RK_ROOT: - e = std::make_unique ("memory-regions", false); + gcc_unreachable (); break; case RK_SYMBOLIC: - e = std::make_unique ("symbolic-region", false); - // TODO + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::other); break; + case RK_DECL: { - e = std::make_unique ("variable", false); + node = make_state_node (diagnostics::state_graphs::node_kind::variable, + make_node_id (reg)); const decl_region &decl_reg = static_cast (reg); + state_node_ref node_ref (*node); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", decl_reg.get_decl ()); - e->set_attr ("name", pp_formatted_text (&pp)); + node_ref.set_name (pp_formatted_text (&pp)); } - set_type_attr (*e, TREE_TYPE (decl_reg.get_decl ())); + set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ())); } break; + case RK_FIELD: - e = std::make_unique ("field", false); - break; case RK_ELEMENT: - e = std::make_unique ("element", false); + /* These should be handled in populate_state_node_for_typed_region. */ + gcc_unreachable (); break; + + case RK_LABEL: case RK_OFFSET: - e = std::make_unique ("offset-region", false); - // TODO - break; case RK_SIZED: - e = std::make_unique ("sized-region", false); - // TODO - break; case RK_CAST: - e = std::make_unique ("cast-region", false); - // TODO - break; - case RK_HEAP_ALLOCATED: - e = std::make_unique ("heap-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; - case RK_ALLOCA: - e = std::make_unique ("alloca-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; case RK_STRING: - e = std::make_unique ("string-region", false); - // TODO - break; case RK_BIT_RANGE: - e = std::make_unique ("RK_BIT_RANGE", false); // TODO - break; case RK_VAR_ARG: - e = std::make_unique ("RK_VAR_ARG", false); // TODO - break; case RK_ERRNO: - e = std::make_unique ("errno", false); - break; case RK_PRIVATE: - e = std::make_unique ("RK_PRIVATE", false); // TODO - break; case RK_UNKNOWN: - e = std::make_unique ("RK_UNKNOWN", false); // TODO + node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id (reg)); break; - } - gcc_assert (e); - set_region_id_attr (*e, reg); + case RK_HEAP_ALLOCATED: + case RK_ALLOCA: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::dynalloc_buffer); + set_attr_for_dynamic_extents (reg, *node); + break; + } + gcc_assert (node); if (reg.get_base_region () == ®) if (!reg.get_type ()) @@ -303,16 +398,17 @@ xml_state::create_element (const region ®) if (search != m_types_for_untyped_regions.end ()) { tree type_to_use = search->second; - set_type_attr (*e, type_to_use); + set_type_attr (*node, type_to_use); } } - return e; + return node; } void -xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all) +analyzer_state_graph:: +create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all) { /* TODO: - symbolic bindings @@ -326,12 +422,12 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, if (auto conc_key = key->dyn_cast_concrete_binding ()) conc_bindings[conc_key->get_bit_range ()] = svalue; if (const region *reg = svalue->maybe_get_region ()) - get_or_create_element (*reg); + get_or_create_state_node (*reg); } - auto &e = get_or_create_element (*cluster.get_base_region ()); + auto ref = get_or_create_state_node (*cluster.get_base_region ()); - e.add_child (create_element_for_conc_bindings (conc_bindings)); + ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings)); const region *typed_reg = cluster.get_base_region (); if (!typed_reg->get_type ()) @@ -346,44 +442,45 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, } if (typed_reg->get_type ()) - populate_element_for_typed_region (e, - *typed_reg, - conc_bindings, - create_all); + populate_state_node_for_typed_region (ref, + *typed_reg, + conc_bindings, + create_all); else { // TODO } } -std::unique_ptr -xml_state::create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings) +std::unique_ptr +analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings) { - auto e = std::make_unique ("concrete-bindings", false); + auto node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("concrete-bindings")); for (auto iter : conc_bindings) { const bit_range bits = iter.first; const svalue *sval = iter.second; - auto binding_element - = std::make_unique ("binding", false); - set_bits_attr (*binding_element, bits); + auto binding_state_node + = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("binding")); + set_bits_attr (*binding_state_node, bits); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; sval->dump_to_pp (&pp, true); - binding_element->set_attr ("value", pp_formatted_text (&pp)); - if (auto svalue_element = create_element_for_svalue (sval)) - binding_element->add_child (std::move (svalue_element)); + binding_state_node->set_attr (STATE_NODE_PREFIX, "value", + pp_formatted_text (&pp)); } - e->add_child (std::move (binding_element)); + node->add_child (std::move (binding_state_node)); } - return e; + return node; } // Try to get the bit_range of REG within its base region bool -xml_state::get_bit_range_within_base_region (const region ®, - bit_range &out) +analyzer_state_graph::get_bit_range_within_base_region (const region ®, + bit_range &out) { region_offset start_offset = reg.get_offset (&m_mgr); if (!start_offset.concrete_p ()) @@ -398,30 +495,28 @@ xml_state::get_bit_range_within_base_region (const region ®, } void -xml_state::populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all) +analyzer_state_graph:: +populate_state_node_for_typed_region (state_node_ref node, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all) { const_tree reg_type = reg.get_type (); gcc_assert (reg_type); - set_type_attr (e, reg_type); + set_type_attr (node, reg_type); bit_range bits (0, 0); if (get_bit_range_within_base_region (reg, bits)) { - set_bits_attr (e, bits); + set_bits_attr (node, bits); auto search = conc_bindings.find (bits); if (search != conc_bindings.end ()) { const svalue *bound_sval = search->second; - if (auto svalue_element = create_element_for_svalue (bound_sval)) - { - xml::printer xp (e); - xp.push_tag ("value-of-region"); - xp.append (std::move (svalue_element)); - } + node.set_json_attr ("value", bound_sval->to_json ()); + if (const region *dst_reg = bound_sval->maybe_get_region ()) + m_pending_edges.push_back ({node, *dst_reg}); } } @@ -454,22 +549,23 @@ xml_state::populate_element_for_typed_region (xml::element &e, = m_mgr.get_element_region (®, const_cast (element_type), sval_index); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - // Here "element" is in the xml sense - auto child_element - = std::make_unique ("element", false); - set_wi_attr (*child_element, "index", idx, UNSIGNED); - set_region_id_attr (*child_element, *child_reg); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::element, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "index", idx, UNSIGNED); + // Recurse: gcc_assert (element_type); - populate_element_for_typed_region (*child_element, - *child_reg, - conc_bindings, - create_all); - e.add_child (std::move (child_element)); + populate_state_node_for_typed_region (*child_state_node, + *child_reg, + conc_bindings, + create_all); + node.m_node.add_child (std::move (child_state_node)); } } } @@ -485,15 +581,17 @@ xml_state::populate_element_for_typed_region (xml::element &e, const bit_range bits (0, item.m_bit_range.m_size_in_bits); const region *child_reg = m_mgr.get_bit_range (®, NULL_TREE, bits); - if (show_child_element_for_child_region_p (*child_reg, - conc_bindings, - create_all)) + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) { - auto child_element - = std::make_unique ("padding", false); - set_wi_attr (*child_element, "num_bits", + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::padding, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "num_bits", item.m_bit_range.m_size_in_bits, SIGNED); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } else @@ -501,26 +599,28 @@ xml_state::populate_element_for_typed_region (xml::element &e, const region *child_reg = m_mgr.get_field_region (®, const_cast (item.m_field)); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - auto child_element - = std::make_unique ("field", false); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::field, + make_node_id (*child_reg)); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%D", item.m_field); - child_element->set_attr ("name", - pp_formatted_text (&pp)); + child_state_node->set_attr (STATE_NODE_PREFIX, "name", + pp_formatted_text (&pp)); } - set_region_id_attr (*child_element, *child_reg); + // Recurse: - populate_element_for_typed_region (*child_element, + populate_state_node_for_typed_region (*child_state_node, *child_reg, conc_bindings, create_all); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } } @@ -530,7 +630,8 @@ xml_state::populate_element_for_typed_region (xml::element &e, } void -xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) +analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, + state_node_ref node_ref) { const svalue *sval = m_state.m_region_model->get_dynamic_extents (®); if (sval) @@ -541,13 +642,13 @@ xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED); else sval->dump_to_pp (&pp, true); - e.set_attr ("dynamic-extents", pp_formatted_text (&pp)); + node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp)); } } bool -xml_state:: -show_child_element_for_child_region_p (const region ®, +analyzer_state_graph:: +show_child_state_node_for_child_region_p (const region ®, const concrete_bindings_t &conc_bindings, bool create_all) { @@ -569,216 +670,18 @@ show_child_element_for_child_region_p (const region ®, return false; } -std::unique_ptr -xml_state::create_element_for_svalue (const svalue *sval) +std::unique_ptr +program_state:: +make_diagnostic_state_graph (const extrinsic_state &ext_state) const { - if (!sval) - return nullptr; - - std::unique_ptr result; - switch (sval->get_kind ()) - { - default: - gcc_unreachable (); - case SK_REGION: - { - const region_svalue *region_sval = (const region_svalue *)sval; - result - = std::make_unique ("pointer-to-region", false); - set_region_id_attr (*result, *region_sval->get_pointee ()); - } - break; - case SK_CONSTANT: - { - const constant_svalue *constant_sval = (const constant_svalue *)sval; - result = std::make_unique ("constant", false); - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - pp_printf (&pp, "%E", constant_sval->get_constant ()); - result->set_attr ("value", pp_formatted_text (&pp)); - } - break; - case SK_UNKNOWN: - result = std::make_unique ("unknown", false); - break; - case SK_POISONED: - { - const poisoned_svalue *poisoned_sval = (const poisoned_svalue *)sval; - switch (poisoned_sval->get_poison_kind ()) - { - default: - gcc_unreachable (); - case poison_kind::uninit: - result = std::make_unique ("uninitialized", false); - break; - case poison_kind::freed: - result = std::make_unique ("freed", false); - break; - case poison_kind::deleted: - result = std::make_unique ("deleted", false); - break; - case poison_kind::popped_stack: - result = std::make_unique ("popped-stack", false); - break; - } - } - break; - case SK_SETJMP: - { - //const setjmp_svalue *setjmp_sval = (const setjmp_svalue *)sval; - result = std::make_unique ("setjmp-buffer", false); - // TODO - } - break; - case SK_INITIAL: - { - const initial_svalue *initial_sval = (const initial_svalue *)sval; - result = std::make_unique ("initial-value-of", false); - set_region_id_attr (*result, *initial_sval->get_region ()); - } - break; - case SK_UNARYOP: - { - const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval; - result = std::make_unique ("unary-op", false); - result->set_attr ("op", get_tree_code_name (unaryop_sval->get_op ())); - result->add_child - (create_element_for_svalue (unaryop_sval->get_arg ())); - } - break; - case SK_BINOP: - { - const binop_svalue *binop_sval = (const binop_svalue *)sval; - result = std::make_unique ("binary-op", false); - result->set_attr ("op", get_tree_code_name (binop_sval->get_op ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg0 ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg1 ())); - } - break; - case SK_SUB: - { - //const sub_svalue *sub_sval = (const sub_svalue *)sval; - result = std::make_unique ("subregion-value", false); - // TODO - } - break; - case SK_REPEATED: - { - const repeated_svalue *repeated_sval = (const repeated_svalue *)sval; - result = std::make_unique ("repeated-value", false); - result->add_child - (create_element_for_svalue (repeated_sval->get_outer_size ())); - result->add_child - (create_element_for_svalue (repeated_sval->get_inner_svalue ())); - } - break; - case SK_BITS_WITHIN: - { - const bits_within_svalue *bits_within_sval - = (const bits_within_svalue *)sval; - result = std::make_unique ("bits-within", false); - set_bits_attr (*result, bits_within_sval->get_bits ()); - result->add_child - (create_element_for_svalue (bits_within_sval->get_inner_svalue ())); - } - break; - case SK_UNMERGEABLE: - { - const unmergeable_svalue *unmergeable_sval - = (const unmergeable_svalue *)sval; - result = std::make_unique ("unmergeable", false); - result->add_child - (create_element_for_svalue (unmergeable_sval->get_arg ())); - } - break; - case SK_PLACEHOLDER: - { - const placeholder_svalue *placeholder_sval - = (const placeholder_svalue *)sval; - result = std::make_unique ("placeholder", false); - result->set_attr ("name", placeholder_sval->get_name ()); - } - break; - case SK_WIDENING: - { - //const widening_svalue *widening_sval = (const widening_svalue *)sval; - result = std::make_unique ("iterating-value", false); - // TODO - } - break; - case SK_COMPOUND: - { - //const compound_svalue *compound_sval = (const compound_svalue *)sval; - result = std::make_unique ("compound-value", false); - // TODO - } - break; - case SK_CONJURED: - { - //const conjured_svalue *conjured_sval = (const conjured_svalue *)sval; - result = std::make_unique ("conjured-value", false); - // TODO - } - break; - case SK_ASM_OUTPUT: - { - /* const asm_output_svalue *asm_output_sval - = (const asm_output_svalue *)sval; */ - result = std::make_unique ("asm-output", false); - // TODO - } - break; - case SK_CONST_FN_RESULT: - { - /* const const_fn_result_svalue *const_fn_result_sval - = (const const_fn_result_svalue *)sval; */ - result = std::make_unique ("const-fn-result", false); - // TODO - } - } - - if (result) - { - if (sval->get_type ()) - set_type_attr (*result, sval->get_type ()); - - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - sval->dump_to_pp (&pp, true); - result->set_attr ("dump-text", pp_formatted_text (&pp)); - } - - return result; -} - -std::unique_ptr -program_state::make_xml (const extrinsic_state &ext_state) const -{ - return std::make_unique (*this, ext_state); -} - -void -program_state::dump_xml_to_pp (const extrinsic_state &ext_state, - pretty_printer *pp) const -{ - auto doc = make_xml (ext_state); - doc->write_as_xml (pp, 0, true); -} - -void -program_state::dump_xml_to_file (const extrinsic_state &ext_state, - FILE *outf) const -{ - pretty_printer pp; - pp.set_output_stream (outf); - dump_xml_to_pp (ext_state, &pp); - pp_flush (&pp); + return std::make_unique (*this, ext_state); } void -program_state::dump_xml (const extrinsic_state &ext_state) const +program_state::dump_sarif (const extrinsic_state &ext_state) const { - dump_xml_to_file (ext_state, stderr); + auto g = make_diagnostic_state_graph (ext_state); + g->dump (); } } // namespace ana diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h index bd6aa4641915..186e19d2f51e 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.h +++ b/gcc/analyzer/ana-state-to-diagnostic-state.h @@ -1,4 +1,4 @@ -/* XML documents for dumping state in an easier-to-read form. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm . @@ -18,41 +18,52 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ -#ifndef GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H -#define GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H +#ifndef GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H +#define GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H -#include "xml.h" +#include "diagnostic-state-graphs.h" +#include "tree-logical-location.h" namespace ana { -class xml_state : public xml::document +class analyzer_state_graph : public diagnostics::digraphs::digraph { public: - xml_state (const program_state &state, - const extrinsic_state &ext_state); - - xml::element & - get_or_create_element (const region ®); + analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state); + diagnostics::state_graphs::state_node_ref + get_or_create_state_node (const region ®); private: - xml::element& - create_and_add_element (const region ®); - - static std::unique_ptr - make_memory_space_element (const char *label); - - std::unique_ptr - create_element (const region ®); + struct pending_edge + { + diagnostics::state_graphs::state_node_ref m_src_node; + const region &m_dst_reg; + }; + + diagnostics::state_graphs::state_node_ref + create_and_add_state_node (const region ®); + + std::unique_ptr + make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id); + + std::unique_ptr + make_memspace_state_node (const region ®, + enum diagnostics::state_graphs::node_kind kind); + + std::unique_ptr + create_state_node (const region ®); /* Spatially sorted concrete bindings. */ typedef std::map concrete_bindings_t; void - create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all); + create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all); - std::unique_ptr - create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings); + std::unique_ptr + create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings); // Try to get the bit_range of REG within its base region bool @@ -60,30 +71,36 @@ private: bit_range &out); void - populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); void - set_attr_for_dynamic_extents (const region ®, xml::element &e); + set_attr_for_dynamic_extents (const region ®, + diagnostics::state_graphs::state_node_ref); bool - show_child_element_for_child_region_p (const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + show_child_state_node_for_child_region_p (const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); + + std::unique_ptr + create_state_node_for_svalue (const svalue *sval); - std::unique_ptr - create_element_for_svalue (const svalue *sval); + std::string make_node_id (const region ®); + std::string make_node_id (const char *prefix); + tree_logical_location_manager m_logical_loc_mgr; const program_state &m_state; const extrinsic_state &m_ext_state; region_model_manager &m_mgr; - xml::element *m_root; - std::map m_region_to_element_map; + std::map m_region_to_state_node_map; std::map m_types_for_untyped_regions; + unsigned m_next_id; + std::vector m_pending_edges; }; } // namespace ana -#endif /* GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H */ +#endif /* GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H */ diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index af336df82447..8cc5ac2b1da3 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "inlining-iterator.h" #include "tree-logical-location.h" #include "diagnostic-format-sarif.h" -#include "xml.h" +#include "diagnostic-state-graphs.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" @@ -225,8 +225,8 @@ checker_event::prepare_for_emission (checker_path *path, print_desc (*pp.get ()); } -std::unique_ptr -checker_event::maybe_make_xml_state (bool debug) const +std::unique_ptr +checker_event::maybe_make_diagnostic_state_graph (bool debug) const { const program_state *state = get_program_state (); if (!state) @@ -235,14 +235,16 @@ checker_event::maybe_make_xml_state (bool debug) const gcc_assert (m_path); const extrinsic_state &ext_state = m_path->get_ext_state (); - auto result = state->make_xml (ext_state); + auto result = state->make_diagnostic_state_graph (ext_state); if (debug) { pretty_printer pp; text_art::theme *theme = global_dc->get_diagram_theme (); text_art::dump_to_pp (*state, theme, &pp); - result->add_comment (pp_formatted_text (&pp)); + result->set_attr (STATE_GRAPH_PREFIX, + "analyzer/program_state/", + pp_formatted_text (&pp)); } return result; diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index 7c44f1e83c8e..cf24e777e29a 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-logical-location.h" #include "analyzer/program-state.h" #include "analyzer/event-loc-info.h" +#include "diagnostic-digraphs.h" namespace ana { @@ -128,12 +129,12 @@ public: virtual bool is_function_entry_p () const { return false; } virtual bool is_return_p () const { return false; } + std::unique_ptr + maybe_make_diagnostic_state_graph (bool debug) const final override; + virtual const program_state * get_program_state () const { return nullptr; } - std::unique_ptr - maybe_make_xml_state (bool debug) const final override; - /* For use with %@. */ const diagnostic_event_id_t *get_id_ptr () const { diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 67024e93d9a4..745ef7e52c83 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1580,9 +1580,9 @@ exploded_node::on_stmt_pre (exploded_graph &eg, state->dump (eg.get_ext_state (), true); return; } - else if (is_special_named_call_p (call, "__analyzer_dump_xml", 0)) + else if (is_special_named_call_p (call, "__analyzer_dump_sarif", 0)) { - state->dump_xml (eg.get_ext_state ()); + state->dump_sarif (eg.get_ext_state ()); return; } else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0)) diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index c0befaca6a4b..85cbe759ee91 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "digraph.h" #include "diagnostic-event-id.h" -#include "diagnostic-state.h" +#include "diagnostic-state-graphs.h" #include "graphviz.h" #include "text-art/tree-widget.h" @@ -1230,8 +1230,14 @@ program_state::make_dump_widget (const text_art::dump_widget_info &dwi) const void program_state::dump_dot (const extrinsic_state &ext_state) const { - auto doc = make_xml (ext_state); - auto graph = make_dot_graph_from_xml_state (*doc); + auto state_graph = make_diagnostic_state_graph (ext_state); + + gcc_assert (global_dc); + auto logical_loc_mgr = global_dc->get_logical_location_manager (); + gcc_assert (logical_loc_mgr); + + auto graph = diagnostics::state_graphs::make_dot_graph (*state_graph, + *logical_loc_mgr); pretty_printer pp; dot::writer w (pp); diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index e2076c136fbc..4278237adc80 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -26,8 +26,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" -namespace xml { class document; } - namespace ana { /* Data shared by all program_state instances. */ @@ -248,11 +246,14 @@ public: void dump (const extrinsic_state &ext_state, bool simple) const; void dump () const; - std::unique_ptr make_xml (const extrinsic_state &ext_state) const; - void dump_xml_to_pp (const extrinsic_state &ext_state, pretty_printer *pp) const; - void dump_xml_to_file (const extrinsic_state &ext_state, FILE *outf) const; - void dump_xml (const extrinsic_state &ext_state) const; - void dump_dot (const extrinsic_state &ext_state) const; + std::unique_ptr + make_diagnostic_state_graph (const extrinsic_state &ext_state) const; + + void + dump_sarif (const extrinsic_state &ext_state) const; + + void + dump_dot (const extrinsic_state &ext_state) const; std::unique_ptr to_json (const extrinsic_state &ext_state) const; diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 3581dbb3949a..2a218d0688be 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -436,9 +436,9 @@ public: const extrinsic_state &ext_state) const; void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const final override; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const final override; standard_deallocator_set m_free; standard_deallocator_set m_scalar_delete; @@ -2735,27 +2735,57 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model, smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state); } +static enum diagnostics::state_graphs::node_dynalloc_state +get_dynalloc_state_for_state (enum resource_state rs) +{ + switch (rs) + { + default: + gcc_unreachable (); + case RS_START: + case RS_NULL: + case RS_NON_HEAP: + case RS_STOP: + return diagnostics::state_graphs::node_dynalloc_state::unknown; + + case RS_ASSUMED_NON_NULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + + case RS_UNCHECKED: + return diagnostics::state_graphs::node_dynalloc_state::unchecked; + case RS_NONNULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + case RS_FREED: + return diagnostics::state_graphs::node_dynalloc_state::freed; + } +} + void -malloc_state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +malloc_state_machine:: +add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { if (const region *reg = sval.maybe_get_region ()) { - auto ®_element = out_xml.get_or_create_element (*reg); + auto reg_node = out_state_graph.get_or_create_state_node (*reg); auto alloc_state = as_a_allocation_state (state); gcc_assert (alloc_state); - reg_element.set_attr ("dynamic-alloc-state", state->get_name ()); + reg_node.set_dynalloc_state + (get_dynalloc_state_for_state (alloc_state->m_rs)); if (alloc_state->m_deallocators) { pretty_printer pp; alloc_state->m_deallocators->dump_to_pp (&pp); - reg_element.set_attr ("expected-deallocators", pp_formatted_text (&pp)); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "expected-deallocators", + pp_formatted_text (&pp)); } if (alloc_state->m_deallocator) - reg_element.set_attr ("deallocator", - alloc_state->m_deallocator->m_name); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "deallocator", + alloc_state->m_deallocator->m_name); } } diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index 840806aa0db3..c93e9c2b67b5 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -161,16 +161,16 @@ state_machine::to_json () const } void -state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +state_machine::add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { // no-op } void -state_machine::add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const +state_machine::add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const { // no-op } diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h index 6298fb663104..4633facac3a2 100644 --- a/gcc/analyzer/sm.h +++ b/gcc/analyzer/sm.h @@ -28,7 +28,7 @@ namespace ana { class state_machine; class sm_context; class pending_diagnostic; -class xml_state; +class analyzer_state_graph; extern bool any_pointer_p (tree expr); extern bool any_pointer_p (const svalue *sval); @@ -188,13 +188,13 @@ public: state_t get_start_state () const { return m_start; } virtual void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const; virtual void - add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const; + add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const; protected: state_t add_state (const char *name); diff --git a/gcc/diagnostic-digraphs.cc b/gcc/diagnostic-digraphs.cc new file mode 100644 index 000000000000..85c0c5166c0a --- /dev/null +++ b/gcc/diagnostic-digraphs.cc @@ -0,0 +1,484 @@ +/* Directed graphs associated with a diagnostic. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#define INCLUDE_ALGORITHM +#define INCLUDE_MAP +#define INCLUDE_SET +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "graphviz.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-format-sarif.h" + +#include "selftest.h" + +using digraph = diagnostics::digraphs::digraph; +using digraph_node = diagnostics::digraphs::node; +using digraph_edge = diagnostics::digraphs::edge; + +namespace { + +class conversion_to_dot +{ +public: + std::unique_ptr + make_dot_graph_from_diagnostic_graph (const digraph &); + + std::unique_ptr + make_dot_node_from_digraph_node (const digraph_node &); + + std::unique_ptr + make_dot_edge_from_digraph_edge (const digraph_edge &); + + dot::id + get_dot_id_for_node (const digraph_node &); + + bool + has_edges_p (const digraph_node &); + +private: + std::set m_nodes_with_edges; + std::map m_node_map; +}; + +} // anonymous namespace + +// class conversion_to_dot + +std::unique_ptr +conversion_to_dot:: +make_dot_graph_from_diagnostic_graph (const diagnostics::digraphs::digraph &input_graph) +{ + auto output_graph = std::make_unique (); + + if (const char *description = input_graph.get_description ()) + output_graph->m_stmt_list.add_attr (dot::id ("label"), + dot::id (description)); + + const int num_nodes = input_graph.get_num_nodes (); + const int num_edges = input_graph.get_num_edges (); + + /* Determine which nodes have in-edges and out-edges. */ + for (int i = 0; i < num_edges; ++i) + { + const digraph_edge &input_edge = input_graph.get_edge (i); + m_nodes_with_edges.insert (&input_edge.get_src_node ()); + m_nodes_with_edges.insert (&input_edge.get_dst_node ()); + } + + for (int i = 0; i < num_nodes; ++i) + { + const digraph_node &input_node = input_graph.get_node (i); + auto dot_node_stmt = make_dot_node_from_digraph_node (input_node); + output_graph->m_stmt_list.add_stmt (std::move (dot_node_stmt)); + } + + for (int i = 0; i < num_edges; ++i) + { + const digraph_edge &input_edge = input_graph.get_edge (i); + auto dot_edge_stmt = make_dot_edge_from_digraph_edge (input_edge); + output_graph->m_stmt_list.add_stmt (std::move (dot_edge_stmt)); + } + + return output_graph; +} + +std::unique_ptr +conversion_to_dot:: +make_dot_node_from_digraph_node (const diagnostics::digraphs::node &input_node) +{ + dot::id dot_id (get_dot_id_for_node (input_node)); + + /* For now, we can only do either edges or children, not both + ...but see https://graphviz.org/docs/attrs/compound/ */ + + if (has_edges_p (input_node)) + { + auto output_node + = std::make_unique (std::move (dot_id)); + m_node_map[&input_node] = output_node.get (); + if (const char *label = input_node.get_label ()) + output_node->set_label (dot::id (label)); + return output_node; + } + else + { + auto output_node = std::make_unique (std::move (dot_id)); + m_node_map[&input_node] = output_node.get (); + if (const char *label = input_node.get_label ()) + output_node->add_attr (dot::id ("label"), dot::id (label)); + const int num_children = input_node.get_num_children (); + for (int i = 0; i < num_children; ++i) + { + const digraph_node &input_child = input_node.get_child (i); + auto dot_child_stmt = make_dot_node_from_digraph_node (input_child); + output_node->m_stmt_list.add_stmt (std::move (dot_child_stmt)); + } + return output_node; + } +} + +std::unique_ptr +conversion_to_dot:: +make_dot_edge_from_digraph_edge (const digraph_edge &input_edge) +{ + const digraph_node &src_dnode = input_edge.get_src_node (); + const digraph_node &dst_dnode = input_edge.get_dst_node (); + auto output_edge + = std::make_unique + (get_dot_id_for_node (src_dnode), + get_dot_id_for_node (dst_dnode)); + if (const char *label = input_edge.get_label ()) + output_edge->set_label (dot::id (label)); + return output_edge; +} + +dot::id +conversion_to_dot::get_dot_id_for_node (const digraph_node &input_node) +{ + if (has_edges_p (input_node)) + return input_node.get_id (); + else + return std::string ("cluster_") + input_node.get_id (); +} + +bool +conversion_to_dot::has_edges_p (const digraph_node &input_node) +{ + return m_nodes_with_edges.find (&input_node) != m_nodes_with_edges.end (); +} + +// class object + +const char * +diagnostics::digraphs::object:: +get_attr (const char *key_prefix, const char *key) const +{ + if (!m_property_bag) + return nullptr; + std::string prefixed_key = std::string (key_prefix) + key; + if (json::value *jv = m_property_bag->get (prefixed_key.c_str ())) + if (json::string *jstr = jv->dyn_cast_string ()) + return jstr->get_string (); + return nullptr; +} + +void +diagnostics::digraphs::object:: +set_attr (const char *key_prefix, const char *key, const char *value) +{ + set_json_attr (key_prefix, key, std::make_unique (value)); +} + +void +diagnostics::digraphs::object:: +set_json_attr (const char *key_prefix, const char *key, std::unique_ptr value) +{ + std::string prefixed_key = std::string (key_prefix) + key; + if (!m_property_bag) + m_property_bag = std::make_unique (); + m_property_bag->set (prefixed_key.c_str (), std::move (value)); +} + +// class digraph + +DEBUG_FUNCTION void +diagnostics::digraphs::digraph::dump () const +{ + make_json_sarif_graph ()->dump (); +} + +std::unique_ptr +diagnostics::digraphs::digraph::make_json_sarif_graph () const +{ + return make_sarif_graph (*this, nullptr, nullptr); +} + +std::unique_ptr +diagnostics::digraphs::digraph::make_dot_graph () const +{ + conversion_to_dot to_dot; + return to_dot.make_dot_graph_from_diagnostic_graph (*this); +} + +std::unique_ptr +diagnostics::digraphs::digraph::clone () const +{ + auto result = std::make_unique (); + + if (get_property_bag ()) + result->set_property_bag (get_property_bag ()->clone_as_object ()); + + std::map node_mapping; + + for (auto &iter : m_nodes) + result->add_node (iter->clone (*result, node_mapping)); + for (auto &iter : m_edges) + result->add_edge (iter->clone (*result, node_mapping)); + + return result; +} + +void +diagnostics::digraphs::digraph::add_edge (const char *id, + node &src_node, + node &dst_node, + const char *label) +{ + auto e = std::make_unique (*this, + id, + src_node, + dst_node); + if (label) + e->set_label (label); + add_edge (std::move (e)); +} + +/* Utility function for edge ids: either use EDGE_ID, or + generate a unique one for when we don't care about the name. + + Edges in SARIF "SHALL" have an id that's unique within the graph + (SARIF 2.1.0 §3.41.2). This is so that graph traversals can refer + to edges by id (SARIF 2.1.0's §3.43.2 edgeId property). */ + +std::string +diagnostics::digraphs::digraph::make_edge_id (const char *edge_id) +{ + /* If we have an id, use it. */ + if (edge_id) + return edge_id; + + /* Otherwise, generate a unique one of the form "edgeN". */ + while (true) + { + auto candidate (std::string ("edge") + + std::to_string (m_next_edge_id_index++)); + auto iter = m_id_to_edge_map.find (candidate); + if (iter != m_id_to_edge_map.end ()) + { + // Try again with the next index... + continue; + } + return candidate; + } +} + +// class node + +DEBUG_FUNCTION void +diagnostics::digraphs::node::dump () const +{ + to_json_sarif_node ()->dump (); +} + +std::unique_ptr +diagnostics::digraphs::node::to_json_sarif_node () const +{ + return make_sarif_node (*this, nullptr, nullptr); +} + +std::unique_ptr +diagnostics::digraphs::node::clone (digraph &new_graph, + std::map &node_mapping) const +{ + auto result + = std::make_unique (new_graph, + get_id ()); + node_mapping.insert ({const_cast (this), result.get ()}); + + result->set_logical_loc (m_logical_loc); + + if (get_property_bag ()) + result->set_property_bag (get_property_bag ()->clone_as_object ()); + + for (auto &iter : m_children) + result->add_child (iter->clone (new_graph, node_mapping)); + + return result; +} + +// class edge + +std::unique_ptr +digraph_edge::clone (digraph &new_graph, + const std::map &node_mapping) const +{ + auto iter_new_src = node_mapping.find (&m_src_node); + gcc_assert (iter_new_src != node_mapping.end ()); + auto iter_new_dst = node_mapping.find (&m_dst_node); + gcc_assert (iter_new_dst != node_mapping.end ()); + auto result + = std::make_unique (new_graph, + m_id.c_str (), + *iter_new_src->second, + *iter_new_dst->second); + if (get_property_bag ()) + result->set_property_bag (get_property_bag ()->clone_as_object ()); + + return result; +} + +DEBUG_FUNCTION void +diagnostics::digraphs::edge::dump () const +{ + to_json_sarif_edge ()->dump (); +} + +std::unique_ptr +diagnostics::digraphs::edge::to_json_sarif_edge () const +{ + return make_sarif_edge (*this, nullptr); +} + +// class lazy_digraph + +const diagnostics::digraphs::digraph & +diagnostics::digraphs::lazy_digraph::get_or_create_digraph () const +{ + if (!m_digraph) + m_digraph = create_digraph (); + gcc_assert (m_digraph); + return *m_digraph; +} + +// class lazy_digraphs + +const std::vector> & +diagnostics::digraphs::lazy_digraphs::get_or_create_digraphs () const +{ + if (!m_digraphs) + m_digraphs = create_digraphs (); + gcc_assert (m_digraphs); + return *m_digraphs; +} + +#if CHECKING_P + +namespace selftest { + +static void +test_empty_graph () +{ + digraph g; + + { + auto sarif = g.make_json_sarif_graph (); + + pretty_printer pp; + sarif->print (&pp, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("{\"nodes\": [],\n" + " \"edges\": []}")); + } + + { + auto dg = g.make_dot_graph (); + + pretty_printer pp; + dot::writer w (pp); + dg->print (w); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("digraph {\n" + "}\n")); + } +} + +static void +test_simple_graph () +{ +#define KEY_PREFIX "/placeholder/" + auto g = std::make_unique (); + g->set_description ("test graph"); + g->set_attr (KEY_PREFIX, "date", "1066"); + + auto a = std::make_unique (*g, "a"); + auto b = std::make_unique (*g, "b"); + b->set_attr (KEY_PREFIX, "color", "red"); + auto c = std::make_unique (*g, "c"); + c->set_label ("I am a node label"); + + auto e = std::make_unique (*g, nullptr, *a, *c); + e->set_attr (KEY_PREFIX, "status", "copacetic"); + e->set_label ("I am an edge label"); + g->add_edge (std::move (e)); + + g->add_node (std::move (a)); + + b->add_child (std::move (c)); + g->add_node (std::move (b)); +#undef KEY_PREFIX + + { + auto sarif = g->make_json_sarif_graph (); + + pretty_printer pp; + sarif->print (&pp, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("{\"properties\": {\"/placeholder/date\": \"1066\"},\n" + " \"nodes\": [{\"id\": \"a\"},\n" + " {\"id\": \"b\",\n" + " \"properties\": {\"/placeholder/color\": \"red\"},\n" + " \"children\": [{\"id\": \"c\"}]}],\n" + " \"edges\": [{\"id\": \"edge0\",\n" + " \"properties\": {\"/placeholder/status\": \"copacetic\"},\n" + " \"sourceNodeId\": \"a\",\n" + " \"targetNodeId\": \"c\"}]}")); + } + + { + auto dg = g->make_dot_graph (); + + pretty_printer pp; + dot::writer w (pp); + dg->print (w); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("digraph {\n" + " label=\"test graph\";\n" + " a;\n" + " \n" + " subgraph cluster_b {\n" + " c [label=\"I am a node label\"];\n" + "\n" + " };\n" + " a -> c [label=\"I am an edge label\"];\n" + "}\n")); + } +} + +/* Run all of the selftests within this file. */ + +void +diagnostic_digraphs_cc_tests () +{ + test_empty_graph (); + test_simple_graph (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/diagnostic-digraphs.h b/gcc/diagnostic-digraphs.h new file mode 100644 index 000000000000..94cb76edd403 --- /dev/null +++ b/gcc/diagnostic-digraphs.h @@ -0,0 +1,411 @@ +/* Directed graphs associated with a diagnostic. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm + +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 +. */ + +#ifndef GCC_DIAGNOSTIC_DIGRAPHS_H +#define GCC_DIAGNOSTIC_DIGRAPHS_H + +#include "json.h" +#include "logical-location.h" + +class graphviz_out; + +class sarif_graph; +class sarif_node; +class sarif_edge; + +namespace dot { class graph; } + +namespace diagnostics { +namespace digraphs { + +/* A family of classes: digraph, node, and edge, closely related to + SARIF's graph, node, and edge types (SARIF v2.1.0 sections 3.39-3.41). + + Nodes can have child nodes, allowing for arbitrarily deep nesting. + Edges can be between any pair of nodes (potentially at different + nesting levels). + + Digraphs, nodes, and edges also optionally have a JSON property bag, + allowing round-tripping of arbitrary key/value pairs through SARIF. */ + +class digraph; +class node; +class edge; + +/* A base class for digraph, node, and edge to allow them to have + an optional JSON property bag. */ + +class object +{ +public: + const char * + get_attr (const char *key_prefix, + const char *key) const; + + void + set_attr (const char *key_prefix, + const char *key, + const char *value); + + void + set_json_attr (const char *key_prefix, + const char *key, + std::unique_ptr value); + + json::object * + get_property_bag () const { return m_property_bag.get (); } + + void + set_property_bag (std::unique_ptr property_bag) + { + m_property_bag = std::move (property_bag); + } + +private: + std::unique_ptr m_property_bag; +}; + +// A directed graph, corresponding to SARIF v2.1.0 section 3.39. + +class digraph : public object +{ + public: + friend class node; + friend class edge; + + digraph () : m_next_edge_id_index (0) {} + virtual ~digraph () {} + + const char * + get_description () const + { + if (!m_description) + return nullptr; + return m_description->c_str (); + } + + void + set_description (const char *desc) + { + if (desc) + m_description = std::make_unique (desc); + else + m_description = nullptr; + } + + node * + get_node_by_id (const char *id) const + { + auto iter = m_id_to_node_map.find (id); + if (iter == m_id_to_node_map.end ()) + return nullptr; + return iter->second; + } + + edge * + get_edge_by_id (const char *id) const + { + auto iter = m_id_to_edge_map.find (id); + if (iter == m_id_to_edge_map.end ()) + return nullptr; + return iter->second; + } + + size_t + get_num_nodes () const + { + return m_nodes.size (); + } + + node & + get_node (size_t idx) const + { + return *m_nodes[idx].get (); + } + + size_t + get_num_edges () const + { + return m_edges.size (); + } + + edge & + get_edge (size_t idx) const + { + return *m_edges[idx].get (); + } + + void + dump () const; + + std::unique_ptr + make_json_sarif_graph () const; + + std::unique_ptr + make_dot_graph () const; + + void + add_node (std::unique_ptr n) + { + gcc_assert (n); + m_nodes.push_back (std::move (n)); + } + + void + add_edge (std::unique_ptr e) + { + gcc_assert (e); + m_edges.push_back (std::move (e)); + } + + void + add_edge (const char *id, + node &src_node, + node &dst_node, + const char *label = nullptr); + + std::unique_ptr clone () const; + + private: + void + add_node_id (std::string node_id, node &new_node) + { + m_id_to_node_map.insert ({std::move (node_id), &new_node}); + } + void + add_edge_id (std::string edge_id, edge &new_edge) + { + m_id_to_edge_map.insert ({std::move (edge_id), &new_edge}); + } + + std::string + make_edge_id (const char *edge_id); + + std::unique_ptr m_description; + std::map m_id_to_node_map; + std::map m_id_to_edge_map; + std::vector> m_nodes; + std::vector> m_edges; + size_t m_next_edge_id_index; +}; + +// A node in a directed graph, corresponding to SARIF v2.1.0 section 3.40. + +class node : public object +{ + public: + virtual ~node () {} + + node (digraph &g, std::string id) + : m_id (id), + m_physical_loc (UNKNOWN_LOCATION) + { + g.add_node_id (std::move (id), *this); + } + node (const node &) = delete; + + std::string + get_id () const { return m_id; } + + const char * + get_label () const + { + if (!m_label) + return nullptr; + return m_label->c_str (); + } + + void + set_label (const char *label) + { + if (label) + m_label = std::make_unique (label); + else + m_label = nullptr; + } + + size_t + get_num_children () const { return m_children.size (); } + + node & + get_child (size_t idx) const { return *m_children[idx].get (); } + + void + add_child (std::unique_ptr child) + { + gcc_assert (child); + m_children.push_back (std::move (child)); + } + + location_t + get_physical_loc () const + { + return m_physical_loc; + } + + void + set_physical_loc (location_t physical_loc) + { + m_physical_loc = physical_loc; + } + + logical_location + get_logical_loc () const + { + return m_logical_loc; + } + + void + set_logical_loc (logical_location logical_loc) + { + m_logical_loc = logical_loc; + } + + void print (graphviz_out &gv) const; + + void + dump () const; + + std::unique_ptr + to_json_sarif_node () const; + + std::unique_ptr + clone (digraph &new_graph, + std::map &node_mapping) const; + + private: + std::string m_id; + std::unique_ptr m_label; + std::vector> m_children; + location_t m_physical_loc; + logical_location m_logical_loc; +}; + +// An edge in a directed graph, corresponding to SARIF v2.1.0 section 3.41. + +class edge : public object +{ + public: + virtual ~edge () {} + + /* SARIF requires us to provide unique edge IDs within a graph, + but otherwise we don't need them. + Pass in nullptr for the id to get the graph to generate a unique + edge id for us. */ + edge (digraph &g, + const char *id, + node &src_node, + node &dst_node) + : m_id (g.make_edge_id (id)), + m_src_node (src_node), + m_dst_node (dst_node) + { + g.add_edge_id (m_id, *this); + } + + std::string + get_id () const { return m_id; } + + const char * + get_label () const + { + if (!m_label) + return nullptr; + return m_label->c_str (); + } + + void + set_label (const char *label) + { + if (label) + m_label = std::make_unique (label); + else + m_label = nullptr; + } + + node & + get_src_node () const { return m_src_node; } + + node & + get_dst_node () const { return m_dst_node; } + + void + dump () const; + + std::unique_ptr + to_json_sarif_edge () const; + + std::unique_ptr + clone (digraph &new_graph, + const std::map &node_mapping) const; + +private: + std::string m_id; + std::unique_ptr m_label; + node &m_src_node; + node &m_dst_node; +}; + +/* Abstract base class for lazily creating + a digraph on demand. + + This allows us to avoid the work of creating the digraph for + the common case where we just have a text sink. */ + +class lazy_digraph +{ +public: + virtual ~lazy_digraph () {} + + const digraph & + get_or_create_digraph () const; + +private: + virtual std::unique_ptr + create_digraph () const = 0; + + mutable std::unique_ptr m_digraph; +}; + +/* Abstract base class for lazily creating a collection of + digraphs on demand. + + This allows us to avoid the work of creating the digraphs for + the common case where we just have a text sink. */ + +class lazy_digraphs +{ +public: + virtual ~lazy_digraphs () {} + + const std::vector> & + get_or_create_digraphs () const; + +private: + virtual std::unique_ptr>> + create_digraphs () const = 0; + + mutable std::unique_ptr>> m_digraphs; +}; + +} // namespace digraphs +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTIC_DIGRAPHS_H */ diff --git a/gcc/diagnostic-format-html.cc b/gcc/diagnostic-format-html.cc index 473880fce245..becac39f5dcc 100644 --- a/gcc/diagnostic-format-html.cc +++ b/gcc/diagnostic-format-html.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-format.h" #include "diagnostic-format-html.h" #include "diagnostic-format-text.h" +#include "diagnostic-format-sarif.h" #include "diagnostic-output-file.h" #include "diagnostic-buffer.h" #include "diagnostic-path.h" @@ -41,7 +42,8 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "xml.h" #include "xml-printer.h" -#include "diagnostic-state.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "graphviz.h" #include "json.h" #include "selftest-xml.h" @@ -52,8 +54,8 @@ html_generation_options::html_generation_options () : m_css (true), m_javascript (true), m_show_state_diagrams (false), - m_show_state_diagram_xml (false), - m_show_state_diagram_dot_src (false) + m_show_state_diagrams_sarif (false), + m_show_state_diagrams_dot_src (false) { } @@ -119,6 +121,8 @@ public: diagnostic_t orig_diag_kind, diagnostic_html_format_buffer *buffer); void emit_diagram (const diagnostic_diagram &diagram); + void emit_global_graph (const diagnostics::digraphs::lazy_digraph &); + void end_group (); std::unique_ptr take_current_diagnostic () @@ -172,6 +176,10 @@ private: void pop_nesting_level (); + void + add_graph (const diagnostics::digraphs::digraph &dg, + xml::element &parent_element); + diagnostic_context &m_context; pretty_printer *m_printer; const line_maps *m_line_maps; @@ -181,6 +189,7 @@ private: std::unique_ptr m_document; xml::element *m_head_element; xml::element *m_title_element; + xml::element *m_body_element; xml::element *m_diagnostics_element; std::unique_ptr m_cur_diagnostic_element; std::vector m_cur_nesting_levels; @@ -401,6 +410,7 @@ html_builder::html_builder (diagnostic_context &context, m_logical_loc_mgr (nullptr), m_head_element (nullptr), m_title_element (nullptr), + m_body_element (nullptr), m_diagnostics_element (nullptr), m_next_diag_id (0), m_last_location (UNKNOWN_LOCATION), @@ -447,6 +457,7 @@ html_builder::html_builder (diagnostic_context &context, { xml::auto_print_element body (xp, "body"); + m_body_element = xp.get_insertion_point (); { auto diagnostics_element = make_div ("gcc-diagnostic-list"); m_diagnostics_element = diagnostics_element.get (); @@ -599,39 +610,45 @@ html_builder::maybe_make_state_diagram (const diagnostic_event &event) if (!m_html_gen_opts.m_show_state_diagrams) return nullptr; - /* Get XML state document; if we're going to print it later, also request + if (!m_logical_loc_mgr) + return nullptr; + + /* Get state graph; if we're going to print it later, also request the debug version. */ - auto xml_state - = event.maybe_make_xml_state (m_html_gen_opts.m_show_state_diagram_xml); - if (!xml_state) + auto state_graph + = event.maybe_make_diagnostic_state_graph + (m_html_gen_opts.m_show_state_diagrams_sarif); + if (!state_graph) return nullptr; // Convert it to .dot AST - auto graph = make_dot_graph_from_xml_state (*xml_state); - gcc_assert (graph); + auto dot_graph + = diagnostics::state_graphs::make_dot_graph (*state_graph, + *m_logical_loc_mgr); + gcc_assert (dot_graph); auto wrapper = std::make_unique ("div", false); xml::printer xp (*wrapper); - if (m_html_gen_opts.m_show_state_diagram_xml) + if (m_html_gen_opts.m_show_state_diagrams_sarif) { - // For debugging, show the XML src inline: + // For debugging, show the SARIF src inline: pretty_printer pp; - xml_state->write_as_xml (&pp, 0, true); + state_graph->make_json_sarif_graph ()->print (&pp, true); print_pre_source (xp, pp_formatted_text (&pp)); } - if (m_html_gen_opts.m_show_state_diagram_dot_src) + if (m_html_gen_opts.m_show_state_diagrams_dot_src) { // For debugging, show the dot src inline: pretty_printer pp; dot::writer w (pp); - graph->print (w); + dot_graph->print (w); print_pre_source (xp, pp_formatted_text (&pp)); } // Turn the .dot into SVG and splice into place - auto svg = dot::make_svg_from_graph (*graph); + auto svg = dot::make_svg_from_graph (*dot_graph); if (svg) xp.append (std::move (svg)); @@ -1125,6 +1142,15 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic, gcc_assert (xp.get_num_open_tags () == depth_within_alert_div); + // Try to display any per-diagnostic graphs + if (diagnostic.metadata) + if (auto ldg = diagnostic.metadata->get_lazy_digraphs ()) + { + auto &digraphs = ldg->get_or_create_digraphs (); + for (auto &dg : digraphs) + add_graph (*dg, *xp.get_insertion_point ()); + } + if (auto patch_element = make_element_for_patch (diagnostic)) { xp.push_tag ("div"); @@ -1221,6 +1247,35 @@ html_builder::emit_diagram (const diagnostic_diagram &/*diagram*/) // TODO: currently a no-op } +void +html_builder::add_graph (const diagnostics::digraphs::digraph &dg, + xml::element &parent_element) +{ + if (auto dot_graph = dg.make_dot_graph ()) + if (auto svg_element = dot::make_svg_from_graph (*dot_graph)) + { + auto div = std::make_unique ("div", false); + div->set_attr ("class", "gcc-directed-graph"); + xml::printer xp (*div); + if (const char *description = dg.get_description ()) + { + xp.push_tag ("h2", true); + xp.add_text (description); + xp.pop_tag ("h2"); + } + xp.append (std::move (svg_element)); + parent_element.add_child (std::move (div)); + } +} + +void +html_builder::emit_global_graph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + auto &dg = ldg.get_or_create_digraph (); + gcc_assert (m_body_element); + add_graph (dg, *m_body_element); +} + /* Implementation of "end_group_cb" for HTML output. */ void @@ -1334,6 +1389,12 @@ public: m_builder.set_printer (*get_printer ()); } + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) final override + { + m_builder.emit_global_graph (ldg); + } + const xml::document &get_document () const { return m_builder.get_document (); diff --git a/gcc/diagnostic-format-html.h b/gcc/diagnostic-format-html.h index 09a97e0ccac7..537ba54ddc52 100644 --- a/gcc/diagnostic-format-html.h +++ b/gcc/diagnostic-format-html.h @@ -36,11 +36,11 @@ struct html_generation_options // If true, attempt to show state diagrams at events bool m_show_state_diagrams; - // If true, show the XML form of the state with such diagrams - bool m_show_state_diagram_xml; + // If true, show the SARIF form of the state with such diagrams + bool m_show_state_diagrams_sarif; // If true, show the .dot source used for the diagram - bool m_show_state_diagram_dot_src; + bool m_show_state_diagrams_dot_src; }; extern diagnostic_output_file diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index 14cdbc2e0cdb..d20ca86ae752 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -28,6 +28,8 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "diagnostic.h" #include "diagnostic-metadata.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "diagnostic-path.h" #include "diagnostic-format.h" #include "diagnostic-buffer.h" @@ -776,6 +778,9 @@ public: void emit_diagram (const diagnostic_diagram &diagram); void end_group (); + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &); + std::unique_ptr take_current_result () { return std::move (m_cur_group_result); @@ -789,7 +794,7 @@ public: const diagnostic_info &diagnostic, enum diagnostic_artifact_role role); std::unique_ptr - make_location_object (sarif_location_manager &loc_mgr, + make_location_object (sarif_location_manager *loc_mgr, const rich_location &rich_loc, logical_location logical_loc, enum diagnostic_artifact_role role); @@ -974,6 +979,8 @@ private: std::unique_ptr> m_cached_logical_locs; + std::unique_ptr> m_run_graphs; + int m_tabstop; std::unique_ptr m_serialization_format; @@ -1305,7 +1312,7 @@ sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic, sometimes these will related to current_function_decl, but often they won't. */ auto location_obj - = builder.make_location_object (*this, *diagnostic.richloc, + = builder.make_location_object (this, *diagnostic.richloc, logical_location (), diagnostic_artifact_role::result_file); auto message_obj @@ -1663,6 +1670,8 @@ sarif_builder::sarif_builder (diagnostic_context &context, m_rules_arr (new json::array ()), m_cached_logical_locs (std::make_unique> ()), + m_run_graphs + (std::make_unique> ()), m_tabstop (context.m_tabstop), m_serialization_format (std::move (serialization_format)), m_sarif_gen_opts (sarif_gen_opts), @@ -1895,6 +1904,17 @@ sarif_builder::end_group () } } +void +sarif_builder:: +report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + auto &dg = ldg.get_or_create_digraph (); + + /* Presumably the location manager must be nullptr; see + https://github.com/oasis-tcs/sarif-spec/issues/712 */ + m_run_graphs->append (make_sarif_graph (dg, this, nullptr)); +} + /* Create a top-level object, and add it to all the results (and other entities) we've seen so far, moving ownership to the object. */ @@ -2049,6 +2069,19 @@ sarif_builder::make_result_object (const diagnostic_info &diagnostic, result_obj->set ("codeFlows", std::move (code_flows_arr)); } + // "graphs" property (SARIF v2.1.0 section 3.27.19). */ + if (diagnostic.metadata) + if (auto ldg = diagnostic.metadata->get_lazy_digraphs ()) + { + auto &digraphs = ldg->get_or_create_digraphs (); + auto graphs_arr = std::make_unique (); + for (auto &iter : digraphs) + graphs_arr->append (make_sarif_graph (*iter, this, + result_obj.get ())); + if (graphs_arr->size () > 0) + result_obj->set ("graphs", std::move (graphs_arr)); + } + /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is set up later, if any nested diagnostics occur within this diagnostic group. */ @@ -2176,7 +2209,7 @@ sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr, logical_loc = client_data_hooks->get_current_logical_location (); auto location_obj - = make_location_object (loc_mgr, *diagnostic.richloc, logical_loc, role); + = make_location_object (&loc_mgr, *diagnostic.richloc, logical_loc, role); /* Don't add entirely empty location objects to the array. */ if (!location_obj->is_empty ()) locations_arr->append (std::move (location_obj)); @@ -2210,10 +2243,12 @@ set_any_logical_locs_arr (sarif_location &location_obj, /* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC and LOGICAL_LOC. Use LOC_MGR for any locations that need "id" values, and for - any worklist items. */ + any worklist items. + Note that we might not always have a LOC_MGR; see + https://github.com/oasis-tcs/sarif-spec/issues/712 */ std::unique_ptr -sarif_builder::make_location_object (sarif_location_manager &loc_mgr, +sarif_builder::make_location_object (sarif_location_manager *loc_mgr, const rich_location &rich_loc, logical_location logical_loc, enum diagnostic_artifact_role role) @@ -2309,8 +2344,8 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr, /* Add related locations for any secondary locations in RICH_LOC that don't have labels (and thus aren't added to "annotations"). */ - if (i > 0 && !handled) - loc_mgr.add_relationship_to_worklist + if (loc_mgr && i > 0 && !handled) + loc_mgr->add_relationship_to_worklist (*location_obj.get (), sarif_location_manager::worklist_item::kind::unlabelled_secondary_location, range->m_loc); @@ -2321,7 +2356,8 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr, std::move (annotations_arr)); } - add_any_include_chain (loc_mgr, *location_obj.get (), loc); + if (loc_mgr) + add_any_include_chain (*loc_mgr, *location_obj.get (), loc); /* A flag for hinting that the diagnostic involves issues at the level of character encodings (such as homoglyphs, or misleading @@ -2799,6 +2835,142 @@ sarif_property_bag::set_logical_location (const char *property_name, builder.make_minimal_sarif_logical_location (logical_loc)); } +static void +copy_any_property_bag (const diagnostics::digraphs::object &input_obj, + sarif_object &output_obj) +{ + if (input_obj.get_property_bag ()) + { + const json::object &old_bag = *input_obj.get_property_bag (); + sarif_property_bag &new_bag = output_obj.get_or_create_properties (); + for (size_t i = 0; i < old_bag.get_num_keys (); ++i) + { + const char *key = old_bag.get_key (i); + json::value *val = old_bag.get (key); + new_bag.set (key, val->clone ()); + } + } +} + +std::unique_ptr +make_sarif_graph (const diagnostics::digraphs::digraph &g, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr) +{ + auto result = std::make_unique (); + + // 3.39.2 description property + if (const char *desc = g.get_description ()) + if (builder) + result->set ("description", + builder->make_message_object (desc)); + + copy_any_property_bag (g, *result); + + // 3.39.3 nodes property + auto nodes_arr = std::make_unique (); + const int num_nodes = g.get_num_nodes (); + for (int i = 0; i < num_nodes; ++i) + nodes_arr->append (make_sarif_node (g.get_node (i), + builder, + sarif_location_mgr)); + result->set ("nodes", std::move (nodes_arr)); + + // 3.39.4 edges property + auto edges_arr = std::make_unique (); + const int num_edges = g.get_num_edges (); + for (int i = 0; i < num_edges; ++i) + edges_arr->append (make_sarif_edge (g.get_edge (i), builder)); + result->set ("edges", std::move (edges_arr)); + + return result; +} + +std::unique_ptr +make_sarif_node (const diagnostics::digraphs::node &n, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr) +{ + auto result = std::make_unique (); + + // 3.40.2 id property + result->set_string ("id", n.get_id ().c_str ()); + + copy_any_property_bag (n, *result); + + // 3.40.3 label property + if (const char *label = n.get_label ()) + if (builder) + result->set ("label", + builder->make_message_object (label)); + + // 3.40.4 location property + if (n.get_logical_loc () + || n.get_physical_loc () != UNKNOWN_LOCATION) + if (builder) + { + rich_location rich_loc + (line_table, n.get_physical_loc ()); + auto loc_obj + = builder->make_location_object + (sarif_location_mgr, + rich_loc, + n.get_logical_loc (), + diagnostic_artifact_role::scanned_file); + result->set ("location", + std::move (loc_obj)); + } + + // 3.40.5 children property + if (const int num_children = n.get_num_children ()) + { + auto children_arr = std::make_unique (); + for (int i = 0; i < num_children; ++i) + children_arr->append (make_sarif_node (n.get_child (i), + builder, + sarif_location_mgr)); + result->set ("children", std::move (children_arr)); + } + + return result; +} + +std::unique_ptr +make_sarif_edge (const diagnostics::digraphs::edge &e, + sarif_builder *builder) +{ + auto result = std::make_unique (); + + // 3.41.2 id property + result->set_string ("id", e.get_id ().c_str ()); + + copy_any_property_bag (e, *result); + + // 3.41.3 label property + if (const char *label = e.get_label ()) + if (builder) + result->set ("label", + builder->make_message_object (label)); + + // 3.41.4 sourceNodeId property + result->set_string ("sourceNodeId", e.get_src_node ().get_id ().c_str ()); + + // 3.41.5 targetNodeId property + result->set_string ("targetNodeId", e.get_dst_node ().get_id ().c_str ()); + + return result; +} + +void +sarif_property_bag::set_graph (const char *property_name, + sarif_builder &builder, + sarif_location_manager *sarif_location_mgr, + const diagnostics::digraphs::digraph &g) +{ + set (property_name, + make_sarif_graph (g, &builder, sarif_location_mgr)); +} + /* Ensure that m_cached_logical_locs has a "logicalLocation" object (SARIF v2.1.0 section 3.33) for K, and return its index within the array. */ @@ -2960,17 +3132,21 @@ populate_thread_flow_location_object (sarif_result &result, via a property bag. */ ev.maybe_add_sarif_properties (*this, tfl_obj); - if (get_opts ().m_xml_state) - if (auto xml_state = ev.maybe_make_xml_state (true)) + if (get_opts ().m_state_graph) + if (auto state_graph = ev.maybe_make_diagnostic_state_graph (true)) { sarif_property_bag &props = tfl_obj.get_or_create_properties (); - pretty_printer pp; - xml_state->write_as_xml (&pp, 0, true); - #define PROPERTY_PREFIX "gcc/diagnostic_event/" - props.set_string (PROPERTY_PREFIX "xml_state", - pp_formatted_text (&pp)); + props.set_graph (PROPERTY_PREFIX "state_graph", + *this, + /* Use RESULT for any related locations in the graph's + nodes. + It's not clear if this is correct; see: + https://github.com/oasis-tcs/sarif-spec/issues/712 + */ + &result, + *state_graph); #undef PROPERTY_PREFIX } @@ -3234,6 +3410,11 @@ make_run_object (std::unique_ptr invocation_obj, std::move (m_cached_logical_locs)); } + // "graphs" property (SARIF v2.1.0 3.14.20) + if (m_run_graphs->size () > 0) + run_obj->set ("graphs", + std::move (m_run_graphs)); + return run_obj; } @@ -3739,6 +3920,12 @@ public: /* No-op. */ } + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &lazy_digraph) final override + { + m_builder.report_global_digraph (lazy_digraph); + } + sarif_builder &get_builder () { return m_builder; } size_t num_results () const { return m_builder.num_results (); } @@ -4117,7 +4304,7 @@ make_sarif_sink (diagnostic_context &context, sarif_generation_options::sarif_generation_options () : m_version (sarif_version::v2_1_0), - m_xml_state (false) + m_state_graph (false) { } @@ -4299,7 +4486,7 @@ test_make_location_object (const sarif_generation_options &sarif_gen_opts, std::unique_ptr location_obj = builder.make_location_object - (result, richloc, logical_location (), + (&result, richloc, logical_location (), diagnostic_artifact_role::analysis_target); ASSERT_NE (location_obj, nullptr); diff --git a/gcc/diagnostic-format-sarif.h b/gcc/diagnostic-format-sarif.h index c3ae330d3e4f..60657c63225c 100644 --- a/gcc/diagnostic-format-sarif.h +++ b/gcc/diagnostic-format-sarif.h @@ -101,7 +101,7 @@ struct sarif_generation_options sarif_generation_options (); enum sarif_version m_version; - bool m_xml_state; + bool m_state_graph; }; extern std::unique_ptr @@ -112,6 +112,14 @@ make_sarif_sink (diagnostic_context &context, diagnostic_output_file output_file); class sarif_builder; +class sarif_location_manager; + +namespace diagnostics { +namespace digraphs { + class digraph; + class node; + class edge; +}} /* Concrete subclass of json::object for SARIF property bags (SARIF v2.1.0 section 3.8). */ @@ -122,6 +130,10 @@ public: void set_logical_location (const char *property_name, sarif_builder &, logical_location logical_loc); + void set_graph (const char *property_name, + sarif_builder &, + sarif_location_manager *sarif_location_mgr, + const diagnostics::digraphs::digraph &g); }; /* Concrete subclass of json::object for SARIF objects that can @@ -136,4 +148,39 @@ public: sarif_property_bag &get_or_create_properties (); }; +/* Subclass of sarif_object for SARIF "graph" objects + (SARIF v2.1.0 section 3.39). */ + +class sarif_graph : public sarif_object +{ +}; + +/* Subclass of sarif_object for SARIF "node" objects + (SARIF v2.1.0 section 3.40). */ + +class sarif_node : public sarif_object +{ +}; + +/* Subclass of sarif_object for SARIF "edge" objects + (SARIF v2.1.0 section 3.41). */ + +class sarif_edge : public sarif_object +{ +}; + +extern std::unique_ptr +make_sarif_graph (const diagnostics::digraphs::digraph &g, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr); + +extern std::unique_ptr +make_sarif_node (const diagnostics::digraphs::node &n, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr); + +extern std::unique_ptr +make_sarif_edge (const diagnostics::digraphs::edge &e, + sarif_builder *builder); + #endif /* ! GCC_DIAGNOSTIC_FORMAT_SARIF_H */ diff --git a/gcc/diagnostic-format-text.h b/gcc/diagnostic-format-text.h index 64f8e13ebe95..64a4452e6ca9 100644 --- a/gcc/diagnostic-format-text.h +++ b/gcc/diagnostic-format-text.h @@ -70,6 +70,12 @@ public: void update_printer () override; + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &) final override + { + // no-op for text + } + /* Helpers for writing lang-specific starters/finalizers for text output. */ char *build_prefix (const diagnostic_info &) const; void report_current_module (location_t where); diff --git a/gcc/diagnostic-format.h b/gcc/diagnostic-format.h index 07db2cd4daa1..a4c776c30b0d 100644 --- a/gcc/diagnostic-format.h +++ b/gcc/diagnostic-format.h @@ -72,6 +72,9 @@ public: Subclasses should update their m_printer accordingly. */ virtual void update_printer () = 0; + virtual void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &) = 0; + diagnostic_context &get_context () const { return m_context; } pretty_printer *get_printer () const { return m_printer.get (); } diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h index baeeb6ead2ed..f6ea7dc16c88 100644 --- a/gcc/diagnostic-metadata.h +++ b/gcc/diagnostic-metadata.h @@ -23,11 +23,20 @@ along with GCC; see the file COPYING3. If not see class sarif_object; +namespace diagnostics { + namespace digraphs { + class lazy_digraphs; + } // namespace digraphs +} // namespace diagnostics + /* A bundle of additional metadata that can be associated with a diagnostic. This supports an optional CWE identifier, and zero or more - "rules". */ + "rules". + + Additionally, this provides a place to associate a diagnostic + with zero or more directed graphs. */ class diagnostic_metadata { @@ -64,7 +73,7 @@ class diagnostic_metadata const char *m_url; }; - diagnostic_metadata () : m_cwe (0) {} + diagnostic_metadata () : m_cwe (0), m_lazy_digraphs (nullptr) {} virtual ~diagnostic_metadata () {} /* Hook for SARIF output to allow for adding diagnostic-specific @@ -87,9 +96,25 @@ class diagnostic_metadata unsigned get_num_rules () const { return m_rules.length (); } const rule &get_rule (unsigned idx) const { return *(m_rules[idx]); } + void + set_lazy_digraphs (const diagnostics::digraphs::lazy_digraphs *lazy_digraphs) + { + m_lazy_digraphs = lazy_digraphs; + } + + const diagnostics::digraphs::lazy_digraphs * + get_lazy_digraphs () const + { + return m_lazy_digraphs; + } + private: int m_cwe; auto_vec m_rules; + + /* An optional way to create directed graphs associated with the + diagnostic, for the sinks that support this (e.g. SARIF). */ + const diagnostics::digraphs::lazy_digraphs *m_lazy_digraphs; }; #endif /* ! GCC_DIAGNOSTIC_METADATA_H */ diff --git a/gcc/diagnostic-output-spec.cc b/gcc/diagnostic-output-spec.cc index 25ef86f5f224..4fa317628122 100644 --- a/gcc/diagnostic-output-spec.cc +++ b/gcc/diagnostic-output-spec.cc @@ -183,7 +183,7 @@ public: private: static sarif_generation_options make_sarif_gen_opts (enum sarif_version version, - bool xml_state); + bool state_graph); static std::unique_ptr make_sarif_serialization_object (enum sarif_serialization_kind); @@ -431,7 +431,7 @@ sarif_scheme_handler::make_sink (const context &ctxt, enum sarif_serialization_kind serialization_kind = sarif_serialization_kind::json; enum sarif_version version = sarif_version::v2_1_0; - bool xml_state = false; + bool state_graph = false; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -469,10 +469,10 @@ sarif_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "xml-state") + if (key == "state-graphs") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - xml_state)) + state_graph)) return nullptr; continue; } @@ -481,8 +481,8 @@ sarif_scheme_handler::make_sink (const context &ctxt, auto_vec known_keys; known_keys.safe_push ("file"); known_keys.safe_push ("serialization"); + known_keys.safe_push ("state-graphs"); known_keys.safe_push ("version"); - known_keys.safe_push ("xml-state"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; @@ -513,7 +513,7 @@ sarif_scheme_handler::make_sink (const context &ctxt, if (!output_file) return nullptr; - auto sarif_gen_opts = make_sarif_gen_opts (version, xml_state); + auto sarif_gen_opts = make_sarif_gen_opts (version, state_graph); auto serialization_obj = make_sarif_serialization_object (serialization_kind); @@ -527,11 +527,11 @@ sarif_scheme_handler::make_sink (const context &ctxt, sarif_generation_options sarif_scheme_handler::make_sarif_gen_opts (enum sarif_version version, - bool xml_state) + bool state_graph) { sarif_generation_options sarif_gen_opts; sarif_gen_opts.m_version = version; - sarif_gen_opts.m_xml_state = xml_state; + sarif_gen_opts.m_state_graph = state_graph; return sarif_gen_opts; } @@ -561,8 +561,8 @@ html_scheme_handler::make_sink (const context &ctxt, label_text filename; bool javascript = true; bool show_state_diagrams = false; - bool show_state_diagram_xml = false; - bool show_state_diagram_dot_src = false; + bool show_state_diagrams_sarif = false; + bool show_state_diagrams_dot_src = false; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -593,17 +593,17 @@ html_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "show-state-diagram-dot-src") + if (key == "show-state-diagrams-dot-src") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagram_dot_src)) + show_state_diagrams_dot_src)) return nullptr; continue; } - if (key == "show-state-diagram-xml") + if (key == "show-state-diagrams-sarif") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagram_xml)) + show_state_diagrams_sarif)) return nullptr; continue; } @@ -615,7 +615,7 @@ html_scheme_handler::make_sink (const context &ctxt, known_keys.safe_push ("javascript"); known_keys.safe_push ("show-state-diagrams"); known_keys.safe_push ("show-state-diagram-dot-src"); - known_keys.safe_push ("show-state-diagram-xml"); + known_keys.safe_push ("show-state-diagram-sarif"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; @@ -649,8 +649,8 @@ html_scheme_handler::make_sink (const context &ctxt, html_gen_opts.m_css = css; html_gen_opts.m_javascript = javascript; html_gen_opts.m_show_state_diagrams = show_state_diagrams; - html_gen_opts.m_show_state_diagram_xml = show_state_diagram_xml; - html_gen_opts.m_show_state_diagram_dot_src = show_state_diagram_dot_src; + html_gen_opts.m_show_state_diagrams_sarif = show_state_diagrams_sarif; + html_gen_opts.m_show_state_diagrams_dot_src = show_state_diagrams_dot_src; auto sink = make_html_sink (dc, *ctxt.get_affected_location_mgr (), diff --git a/gcc/diagnostic-path.cc b/gcc/diagnostic-path.cc index 3e169daa8cd1..1ae87b7e0bce 100644 --- a/gcc/diagnostic-path.cc +++ b/gcc/diagnostic-path.cc @@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "diagnostic.h" #include "diagnostic-path.h" -#include "xml.h" +#include "diagnostic-state-graphs.h" /* Disable warnings about missing quoting in GCC diagnostics for the print calls below. */ @@ -156,12 +156,12 @@ diagnostic_event::get_desc (pretty_printer &ref_pp) const return label_text::take (xstrdup (pp_formatted_text (pp.get ()))); } -// Base implementation of diagnostic_event::maybe_make_xml_state +// Base implementation of diagnostic_event::maybe_make_diagnostic_state_graph -std::unique_ptr -diagnostic_event::maybe_make_xml_state (bool) const +std::unique_ptr +diagnostic_event::maybe_make_diagnostic_state_graph (bool) const { - // Don't attempt to make a state document: + // Don't attempt to make a state graph: return nullptr; } diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h index e68f7681a5eb..ca2628eda691 100644 --- a/gcc/diagnostic-path.h +++ b/gcc/diagnostic-path.h @@ -30,6 +30,12 @@ namespace xml { class document; } class sarif_builder; class sarif_object; +namespace diagnostics { +namespace digraphs { + class digraph; +} // namespace digraphs +} //namespace diagnostics + /* A diagnostic_path is an optional additional piece of metadata associated with a diagnostic (via its rich_location). @@ -173,9 +179,9 @@ class diagnostic_event } /* Hook for capturing state at this event, potentially for visualizing - in HTML output. */ - virtual std::unique_ptr - maybe_make_xml_state (bool debug) const; + in HTML output, or for adding to SARIF. */ + virtual std::unique_ptr + maybe_make_diagnostic_state_graph (bool debug) const; label_text get_desc (pretty_printer &ref_pp) const; }; diff --git a/gcc/diagnostic-state-graphs.cc b/gcc/diagnostic-state-graphs.cc new file mode 100644 index 000000000000..62eb6f5704a3 --- /dev/null +++ b/gcc/diagnostic-state-graphs.cc @@ -0,0 +1,158 @@ +/* Extensions to diagnostics::digraphs to support state graphs. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#define INCLUDE_ALGORITHM +#define INCLUDE_MAP +#define INCLUDE_SET +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "diagnostic-state-graphs.h" +#include "graphviz.h" +#include "xml.h" +#include "xml-printer.h" +#include "intl.h" +#include "selftest.h" + +using namespace diagnostics::state_graphs; + +const char * const node_kind_strs[] = { + "globals", + "code", + "function", + "stack", + "stack-frame", + "heap", + "thread-local", + "dynalloc-buffer", + "variable", + "field", + "padding", + "element", + "other", +}; + +const char * +diagnostics::state_graphs::node_kind_to_str (enum node_kind k) +{ + return node_kind_strs[static_cast (k)]; +} + +// struct state_node_ref + +enum node_kind +state_node_ref::get_node_kind () const +{ + const char *value = get_attr ("kind"); + if (!value) + return node_kind::other; + + for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i) + if (!strcmp (node_kind_strs[i], value)) + return static_cast (i); + + return node_kind::other; +} + +void +state_node_ref::set_node_kind (enum node_kind k) +{ + set_attr ("kind", node_kind_to_str (k)); +} + +const char * const dynalloc_state_strs[] = { + "unknown", + "nonnull", + "unchecked", + "freed" +}; + +enum node_dynalloc_state +state_node_ref::get_dynalloc_state () const +{ + const char *value = get_attr ("dynalloc-state"); + if (!value) + return node_dynalloc_state::unknown; + + for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i) + if (!strcmp (dynalloc_state_strs[i], value)) + return static_cast (i); + + return node_dynalloc_state::unknown; +} + +void +state_node_ref::set_dynalloc_state (enum node_dynalloc_state s) const +{ + set_attr ("dynalloc-state", + dynalloc_state_strs[static_cast (s)]); +} + +const char * +state_node_ref::get_dynamic_extents () const +{ + return m_node.get_attr (STATE_NODE_PREFIX, "dynamic-extents"); +} + +void +state_node_ref::set_json_attr (const char *key, + std::unique_ptr value) const +{ + m_node.set_json_attr (STATE_NODE_PREFIX, key, std::move (value)); +} + +#if CHECKING_P + +namespace selftest { + +static void +test_node_attrs () +{ + diagnostics::digraphs::digraph g; + diagnostics::digraphs::node n (g, "a"); + state_node_ref node_ref (n); + + ASSERT_EQ (node_ref.get_node_kind (), node_kind::other); + node_ref.set_node_kind (node_kind::stack); + ASSERT_EQ (node_ref.get_node_kind (), node_kind::stack); + + ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::unknown); + node_ref.set_dynalloc_state (node_dynalloc_state::freed); + ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::freed); + + ASSERT_EQ (node_ref.get_type (), nullptr); + node_ref.set_type ("const char *"); + ASSERT_STREQ (node_ref.get_type (), "const char *"); +} + +/* Run all of the selftests within this file. */ + +void +diagnostic_state_graphs_cc_tests () +{ + test_node_attrs (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/diagnostic-state-graphs.h b/gcc/diagnostic-state-graphs.h new file mode 100644 index 000000000000..def6d0588971 --- /dev/null +++ b/gcc/diagnostic-state-graphs.h @@ -0,0 +1,156 @@ +/* Extensions to diagnostics::digraphs to support state graphs. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm + +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 +. */ + +#ifndef GCC_DIAGNOSTIC_STATE_GRAPHS_H +#define GCC_DIAGNOSTIC_STATE_GRAPHS_H + +#include "diagnostic-digraphs.h" +#include "logical-location.h" + +/* diagnostics::digraphs provides support for directed graphs. + + diagnostics::state_graphs provides a way to extend these graphs + for representing "state graphs" i.e. a representation of the state + of memory inside a program, for use e.g. by -fanalyzer. + + Specifically, nodes represent memory regions, and we use property bags + in these nodes to stash extra properties (e.g. what kind of memory region + a node is e.g. stack vs heap). */ + +class sarif_graph; +namespace dot { class graph; } + +namespace diagnostics { +namespace state_graphs { + +enum class node_kind +{ + // Memory regions + globals, + code, + function, // code within a particular function + stack, + stack_frame, + heap_, + thread_local_, + + /* Dynamically-allocated buffer, + on heap or stack (depending on parent). */ + dynalloc_buffer, + + variable, + + field, // field within a struct or union + padding, // padding bits in a struct or union + element, // element within an array + + other // anything else +}; + +extern const char * +node_kind_to_str (enum node_kind); + +enum class node_dynalloc_state +{ + unknown, + nonnull, + unchecked, + freed +}; + +/* Prefixes to use in SARIF property bags. */ +#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/" +#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/" +#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/" + +/* A wrapper around a node that gets/sets attributes, using + the node's property bag for storage, so that the data roundtrips + through SARIF. */ + +struct state_node_ref +{ + state_node_ref (diagnostics::digraphs::node &node) + : m_node (node) + {} + + enum node_kind + get_node_kind () const; + void + set_node_kind (enum node_kind); + + // For node_kind::stack_frame, this will be the function + logical_location + get_logical_loc () const + { + return m_node.get_logical_loc (); + } + + // For node_kind::dynalloc_buffer + enum node_dynalloc_state + get_dynalloc_state () const; + + void + set_dynalloc_state (enum node_dynalloc_state) const; + + const char * + get_dynamic_extents () const; + + const char * + get_name () const { return get_attr ("name"); } + void + set_name (const char *name) const { set_attr ("name", name); } + + const char * + get_type () const { return get_attr ("type"); } + void + set_type (const char *type) const { set_attr ("type", type); } + + const char * + get_value () const { return get_attr ("value"); } + + const char * + get_index () const { return get_attr ("index"); } + + const char * + get_attr (const char *key) const + { + return m_node.get_attr (STATE_NODE_PREFIX, key); + } + + void + set_attr (const char *key, const char *value) const + { + return m_node.set_attr (STATE_NODE_PREFIX, key, value); + } + + void + set_json_attr (const char *key, std::unique_ptr value) const; + + diagnostics::digraphs::node &m_node; +}; + +extern std::unique_ptr +make_dot_graph (const diagnostics::digraphs::digraph &state_graph, + const logical_location_manager &logical_loc_mgr); + +} // namespace state_graphs +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTIC_STATE_GRAPHS_H */ diff --git a/gcc/diagnostic-state-to-dot.cc b/gcc/diagnostic-state-to-dot.cc index ddae83b85cd2..8195c1148fe0 100644 --- a/gcc/diagnostic-state-to-dot.cc +++ b/gcc/diagnostic-state-to-dot.cc @@ -1,4 +1,4 @@ -/* Creating GraphViz .dot files from XML state documents. +/* Creating GraphViz .dot files from diagnostic state graphs. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm . @@ -27,81 +27,59 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" +#include "diagnostic-state-graphs.h" +#include "graphviz.h" #include "xml.h" #include "xml-printer.h" -#include "graphviz.h" +#include "intl.h" + +using namespace diagnostics::state_graphs; static int -get_depth (const xml::element &e) +get_depth (const diagnostics::digraphs::node &n) { int deepest_child = 0; - for (auto &iter : e.m_children) - if (xml::element *child_element = iter->dyn_cast_element ()) + for (size_t i = 0; i < n.get_num_children (); ++i) deepest_child = std::max (deepest_child, - get_depth (*child_element)); + get_depth (n.get_child (i))); return deepest_child + 1; } -enum class dynalloc_state -{ - unknown, - nonnull, - unchecked, - freed -}; - static const char * -get_color_for_dynalloc_state (enum dynalloc_state dynalloc_st) +get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st) { switch (dynalloc_st) { default: gcc_unreachable (); break; - case dynalloc_state::unknown: - case dynalloc_state::nonnull: + case node_dynalloc_state::unknown: + case node_dynalloc_state::nonnull: return nullptr; - case dynalloc_state::unchecked: + case node_dynalloc_state::unchecked: return "#ec7a08"; // pf-orange-400 - case dynalloc_state::freed: + case node_dynalloc_state::freed: return "#cc0000"; // pf-red-100 } } static void set_color_for_dynalloc_state (dot::attr_list &attrs, - enum dynalloc_state dynalloc_state) + enum node_dynalloc_state state) { - if (const char *color = get_color_for_dynalloc_state (dynalloc_state)) + if (const char *color = get_color_for_dynalloc_state (state)) attrs.add (dot::id ("color"), dot::id (color)); } -static enum dynalloc_state -get_dynalloc_state (const xml::element &input_element) -{ - const char *dyn_alloc_state = input_element.get_attr ("dynamic-alloc-state"); - if (!dyn_alloc_state) - return dynalloc_state::unknown; - - if (dyn_alloc_state == std::string ("unchecked")) - return dynalloc_state::unchecked; - - if (dyn_alloc_state == std::string ("nonnull")) - return dynalloc_state::nonnull; - - if (dyn_alloc_state == std::string ("freed")) - return dynalloc_state::freed; - - return dynalloc_state::unknown; -} - class state_diagram : public dot::graph { public: - state_diagram (const xml::document &input_state_doc) - : m_next_id (0), + state_diagram (const diagnostics::digraphs::digraph &input_state_graph, + const logical_location_manager &logical_loc_mgr) + : m_logical_loc_mgr (logical_loc_mgr), + // m_next_id (0), m_show_tags (false) { // "node [shape=plaintext]\n" @@ -112,41 +90,46 @@ public: add_stmt (std::move (attr_stmt)); } - /* Recurse down the XML state diagram, creating subgraphs + /* Determine which nodes are involved in edges. */ + for (size_t i = 0; i < input_state_graph.get_num_edges (); ++i) + { + auto &edge = input_state_graph.get_edge (i); + m_src_nodes.insert (&edge.get_src_node ()); + m_dst_nodes.insert (&edge.get_dst_node ()); + } + + /* Recurse down the nodes in the state graph, creating subgraphs and then eventually creating nodes, and recursively - creating XML tables, adding ports for the endpoints of edges, - and recording edges we'll want to create (into m_pending_edges). */ - xml::element *input_elmt_state_diagram - = input_state_doc.find_child_element ("state-diagram"); - gcc_assert (input_elmt_state_diagram); - xml::element *input_elmt_mem_regions - = input_elmt_state_diagram->find_child_element ("memory-regions"); - if (!input_elmt_mem_regions) - return; + creating XML tables, and adding ports for the endpoints of edges + where needed. */ + auto root_cluster = std::make_unique (dot::id ("cluster_memory_regions")); - for (auto &iter : input_elmt_mem_regions->m_children) - on_input_xml_node (*root_cluster, *iter); + for (size_t i = 0; i < input_state_graph.get_num_nodes (); ++i) + on_input_state_node (*root_cluster, + state_node_ref (input_state_graph.get_node (i))); add_stmt (std::move (root_cluster)); - /* We should now have ports for edge endpoints for all region ids. - Use them now to create edges. */ - for (auto &pe : m_pending_edges) + /* Now create dot edges for edges in input_stage_graph. */ + for (size_t i = 0; i < input_state_graph.get_num_edges (); ++i) { - auto search = m_region_id_to_dst_node_id.find (pe.m_dst_region_id); - if (search != m_region_id_to_dst_node_id.end ()) - { - auto &dst_node_id = search->second; - auto e = std::make_unique (pe.m_src_node_id, - dst_node_id); - - auto dynalloc_state = m_region_id_to_dynalloc_state.find (pe.m_dst_region_id); - if (dynalloc_state != m_region_id_to_dynalloc_state.end ()) - set_color_for_dynalloc_state (e->m_attrs, - dynalloc_state->second); - - add_stmt (std::move (e)); - } + auto &edge = input_state_graph.get_edge (i); + auto &src_node = edge.get_src_node (); + auto &dst_node = edge.get_dst_node (); + + auto src_port_id = m_src_node_to_port_id.find (&src_node); + if (src_port_id == m_src_node_to_port_id.end ()) + continue; + auto dst_port_id = m_dst_node_to_port_id.find (&dst_node); + if (dst_port_id == m_dst_node_to_port_id.end ()) + continue; + + auto e = std::make_unique (src_port_id->second, + dst_port_id->second); + set_color_for_dynalloc_state + (e->m_attrs, state_node_ref (dst_node).get_dynalloc_state ()); + + add_stmt (std::move (e)); } } @@ -165,35 +148,58 @@ private: } dot::id - make_id (bool cluster = false) + make_id (state_node_ref state_node, bool cluster) { + std::string input_node_id = state_node.m_node.get_id (); if (cluster) - return std::string ("cluster_") + std::to_string (m_next_id++); + return std::string ("cluster_") + input_node_id; else - return std::string ("id_") + std::to_string (m_next_id++); + return input_node_id; } bool - starts_node_p (const xml::element &e) + starts_node_p (state_node_ref state_node) { - if (e.m_kind == "stack" - || e.m_kind == "heap-buffer" - || e.m_kind == "variable") // e.g. within globals - return true; - return false; + switch (state_node.get_node_kind ()) + { + default: + return false; + + case node_kind::stack: + /* We want all frames in the stack in the same table, + so they are grouped. */ + case node_kind::dynalloc_buffer: + case node_kind::variable: + return true; + } } - void - on_input_xml_node (dot::subgraph &parent_subgraph, - xml::node &input_node) + const char * + get_label_for_node (state_node_ref state_node) { - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; + switch (state_node.get_node_kind ()) + { + default: + return nullptr; + + case node_kind::globals: + return _("Globals"); + case node_kind::code: + return _("Code"); + case node_kind::stack: + return _("Stack"); + case node_kind::heap_: + return _("Heap"); + } + } - dot::id sg_id = make_id (true); + void + on_input_state_node (dot::subgraph &parent_subgraph, + state_node_ref state_node) + { + dot::id sg_id = make_id (state_node, true); - if (starts_node_p (*input_element)) + if (starts_node_p (state_node)) { // Create node with table xml::element table ("table", false); @@ -202,14 +208,14 @@ private: xp.set_attr ("cellborder", "1"); xp.set_attr ("cellspacing", "0"); - const int max_depth = get_depth (*input_element); + const int max_depth = get_depth (state_node.m_node); const int num_columns = max_depth + 2; - dot::id id_of_node = make_id (); - on_xml_node (id_of_node, xp, *input_element, - max_depth, 0, num_columns); + dot::id id_of_dot_node = make_id (state_node, false); + on_node_in_table (id_of_dot_node, xp, state_node, + max_depth, 0, num_columns); - auto node = std::make_unique (std::move (id_of_node)); + auto node = std::make_unique (std::move (id_of_dot_node)); node->m_attrs.add (dot::id ("shape"), dot::id ("plaintext")); @@ -224,12 +230,13 @@ private: { auto child_subgraph = std::make_unique (std::move (sg_id)); - if (const char *label = input_element->get_attr ("label")) + if (const char *label = get_label_for_node (state_node)) child_subgraph->add_attr (dot::id ("label"), dot::id (label)); // recurse: - for (auto &iter : input_element->m_children) - on_input_xml_node (*child_subgraph, *iter); + for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + on_input_state_node (*child_subgraph, + state_node.m_node.get_child (i)); parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph)); } } @@ -237,13 +244,13 @@ private: enum class style { h1, h2 }; void - add_title_tr (const dot::id &id_of_node, + add_title_tr (const dot::id &id_of_dot_node, xml::printer &xp, int num_columns, - const xml::element &input_element, + state_node_ref state_node, std::string heading, enum style styl, - enum dynalloc_state dynalloc_state) + enum node_dynalloc_state dynalloc_state) { xp.push_tag ("tr", true); xp.push_tag ("td", false); @@ -282,183 +289,194 @@ private: xp.add_text (std::move (heading)); xp.pop_tag ("font"); - maybe_add_dst_port (id_of_node, xp, input_element); + maybe_add_dst_port (id_of_dot_node, xp, state_node); xp.pop_tag ("td"); xp.pop_tag ("tr"); } - /* Recursively add to XP for INPUT_NODE and its descendents. */ + /* Recursively add to XP for STATE_NODE and its descendents. */ void - on_xml_node (const dot::id &id_of_node, - xml::printer &xp, - xml::node &input_node, - int max_depth, - int depth, - int num_columns) + on_node_in_table (const dot::id &id_of_dot_node, + xml::printer &xp, + state_node_ref state_node, + int max_depth, + int depth, + int num_columns) { bool recurse = true; + auto input_node_kind = state_node.get_node_kind (); - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; - - if (input_element->m_kind == "concrete-bindings") - return; - if (input_element->m_kind == "padding") - return; - - if (input_element->m_kind == "stack") - { - add_title_tr (id_of_node, xp, num_columns, *input_element, "Stack", - style::h1, dynalloc_state::unknown); - } - else if (input_element->m_kind == "stack-frame") - { - if (const char *function = input_element->get_attr ("function")) - add_title_tr (id_of_node, xp, num_columns, *input_element, - std::string ("Frame: ") + function, - style::h2, dynalloc_state::unknown); - } - else if (input_element->m_kind == "heap-buffer") + switch (input_node_kind) { - const char *extents = input_element->get_attr ("dynamic-extents"); - enum dynalloc_state dynalloc_st = get_dynalloc_state (*input_element); - if (auto region_id = input_element->get_attr ("region_id")) - m_region_id_to_dynalloc_state[region_id] = dynalloc_st; - const char *type = input_element->get_attr ("type"); - pretty_printer pp; - switch (dynalloc_st) - { - default: - gcc_unreachable (); - - case dynalloc_state::unknown: - case dynalloc_state::nonnull: - if (type) - { + case node_kind::padding: + case node_kind::other: + return; + + case node_kind::stack: + add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack", + style::h1, + node_dynalloc_state::unknown); + break; + case node_kind::stack_frame: + if (auto logical_loc = state_node.get_logical_loc ()) + if (const char *function + = m_logical_loc_mgr.get_short_name (logical_loc)) + add_title_tr (id_of_dot_node, xp, num_columns, state_node, + std::string ("Frame: ") + function, + style::h2, + node_dynalloc_state::unknown); + break; + case node_kind::dynalloc_buffer: + { + enum node_dynalloc_state dynalloc_st + = state_node.get_dynalloc_state (); + const char *extents = state_node.get_dynamic_extents (); + const char *type = state_node.get_type (); + pretty_printer pp; + switch (dynalloc_st) + { + default: + gcc_unreachable (); + + case node_dynalloc_state::unknown: + case node_dynalloc_state::nonnull: + if (type) + { if (extents) pp_printf (&pp, "%s (%s byte allocation)", type, extents); else pp_printf (&pp, "%s", type); - } - else + } + else + { + if (extents) + pp_printf (&pp, "%s byte allocation", + extents); + } + break; + + case node_dynalloc_state::unchecked: + if (type) + { + if (extents) + pp_printf (&pp, "%s (unchecked %s byte allocation)", + type, extents); + } + else + { + if (extents) + pp_printf (&pp, "Unchecked %s byte allocation", + extents); + } + break; + + case node_dynalloc_state::freed: + // TODO: show deallocator + // TODO: show deallocation event + pp_printf (&pp, "Freed buffer"); + break; + } + maybe_add_dst_port (id_of_dot_node, xp, state_node); + add_title_tr (id_of_dot_node, xp, num_columns, state_node, + pp_formatted_text (&pp), + style::h2, + dynalloc_st); + } + break; + + default: + { + xp.push_tag ("tr", true); + + maybe_add_dst_port (id_of_dot_node, xp, state_node); + + if (depth > 0) + { + /* Indent, by create a spanning "depth" columns. */ + xp.push_tag ("td", false); + xp.set_attr ("colspan", std::to_string (depth)); + xp.add_text (" "); // graphviz doesn't like + xp.pop_tag ("td"); + } + + switch (input_node_kind) + { + default: + break; + case node_kind::variable: { - if (extents) - pp_printf (&pp, "%s byte allocation", - extents); + const char *name = state_node.get_name (); + gcc_assert (name); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text (name); + pop_src_text (xp); + xp.pop_tag ("td"); } - break; - - case dynalloc_state::unchecked: - if (type) + break; + case node_kind::element: { - if (extents) - pp_printf (&pp, "%s (unchecked %s byte allocation)", - type, extents); + const char *index = state_node.get_index (); + gcc_assert (index); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text ("["); + xp.add_text (index); + xp.add_text ("]"); + pop_src_text (xp); + xp.pop_tag ("td"); } - else + break; + case node_kind::field: { - if (extents) - pp_printf (&pp, "Unchecked %s byte allocation", - extents); + const char *name = state_node.get_name (); + gcc_assert (name); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text ("."); + xp.add_text (name); + pop_src_text (xp); + xp.pop_tag ("td"); } - break; - - case dynalloc_state::freed: - // TODO: show deallocator - // TODO: show deallocation event - pp_printf (&pp, "Freed buffer"); - break; - } - add_title_tr (id_of_node, xp, num_columns, *input_element, - pp_formatted_text (&pp), - style::h2, - dynalloc_st); - } - else - { - xp.push_tag ("tr", true); - if (depth > 0) - { - /* Indent, by create a spanning "depth" columns. */ - xp.push_tag ("td", false); - xp.set_attr ("colspan", std::to_string (depth)); - xp.add_text (" "); // graphviz doesn't like - xp.pop_tag ("td"); - } - if (m_show_tags) - { - // Debug: show XML tag - xp.push_tag ("td", false); - xp.add_text ("<"); - xp.add_text (input_element->m_kind); - xp.add_text (">"); - xp.pop_tag ("td"); - } - if (input_element->m_kind == "variable") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "element") - { - const char *index = input_element->get_attr ("index"); - gcc_assert (index); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("["); - xp.add_text (index); - xp.add_text ("]"); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "field") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("."); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (const char *type = input_element->get_attr ("type")) - { - xp.push_tag ("td", false); - if (max_depth > depth) - xp.set_attr ("colspan", std::to_string (max_depth - depth)); - xp.set_attr ("align", "right"); - push_src_text (xp); - xp.add_text (type); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (auto value = input_element->find_child_element ("value-of-region")) - { - xp.push_tag ("td", false); - for (auto &iter : value->m_children) - if (auto child_element = iter->dyn_cast_element ()) - print_value (id_of_node, xp, *child_element); - xp.pop_tag ("td"); - recurse = false; - } - xp.pop_tag ("tr"); + break; + } + + if (const char *type = state_node.get_type ()) + { + xp.push_tag ("td", false); + xp.set_attr ("align", "right"); + push_src_text (xp); + xp.add_text (type); + pop_src_text (xp); + xp.pop_tag ("td"); + } + + if (const char *value = state_node.get_value ()) + { + xp.push_tag ("td", false); + xp.set_attr ("align", "left"); + maybe_add_src_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text (value); + pop_src_text (xp); + xp.pop_tag ("td"); + recurse = false; + } + xp.pop_tag ("tr"); + } + break; } if (recurse) - for (auto &iter : input_element->m_children) - on_xml_node (id_of_node, xp, *iter, max_depth, depth + 1, num_columns); + for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + on_node_in_table (id_of_dot_node, xp, + state_node.m_node.get_child (i), + max_depth, depth + 1, num_columns); } void @@ -474,64 +492,63 @@ private: xp.pop_tag ("font"); } + /* If STATE_NODE is in m_src_nodes, add a port to XP for possible + incoming edges to use. */ + void - print_value (const dot::id &id_of_node, - xml::printer &xp, - xml::element &input_element) + maybe_add_src_port (const dot::id &id_of_dot_node, + xml::printer &xp, + state_node_ref state_node) { - if (input_element.m_kind == "pointer-to-region") - if (const char *dst_region_id = input_element.get_attr ("region_id")) - { - dot::id src_port_id = make_id (); - xp.set_attr ("port", src_port_id.m_str); - m_pending_edges.push_back - ({dot::node_id (id_of_node, - dot::port (src_port_id, - dot::compass_pt::e)), - dst_region_id}); - } - - if (input_element.m_kind == "uninitialized") - { - xp.add_text ("(uninitialized)"); - return; - } + auto iter = m_src_nodes.find (&state_node.m_node); + if (iter == m_src_nodes.end ()) + return; - if (auto dump_text = input_element.get_attr ("dump-text")) - xp.add_text (dump_text); + dot::id src_id = make_id (state_node, false); + dot::node_id node_id (id_of_dot_node, + dot::port (src_id, + dot::compass_pt::e)); + m_src_node_to_port_id.insert ({&state_node.m_node, node_id}); + xp.set_attr ("port", src_id.m_str); } - /* If INPUT_ELEMENT has a "region_id", add a port to XP for possible + /* If STATE_NODE is in m_dst_nodes, add a port to XP for possible incoming edges to use. */ void - maybe_add_dst_port (const dot::id &id_of_node, + maybe_add_dst_port (const dot::id &id_of_dot_node, xml::printer &xp, - const xml::element &input_element) + state_node_ref state_node) { - if (const char *region_id = input_element.get_attr ("region_id")) - { - dot::id dst_id = make_id (); - dot::node_id node_id (id_of_node, - dot::port (dst_id/*, - dot::compass_pt::w*/)); - xp.set_attr ("port", dst_id.m_str); - m_region_id_to_dst_node_id.emplace (std::string (region_id), - std::move (node_id)); - } - } + auto iter = m_dst_nodes.find (&state_node.m_node); + if (iter == m_dst_nodes.end ()) + return; + dot::id dst_id = make_id (state_node, false); + dot::node_id node_id (id_of_dot_node, + dot::port (dst_id/*, + dot::compass_pt::w*/)); + m_dst_node_to_port_id.insert ({&state_node.m_node, node_id}); + xp.set_attr ("port", dst_id.m_str); + } private: - int m_next_id; - std::vector m_pending_edges; - std::map m_region_id_to_dst_node_id; - std::map m_region_id_to_dynalloc_state; + const logical_location_manager &m_logical_loc_mgr; + + /* All nodes involved in edges (and thus will need a port). */ + std::set m_src_nodes; + std::set m_dst_nodes; + + std::map m_src_node_to_port_id; + std::map m_dst_node_to_port_id; + bool m_show_tags; }; std::unique_ptr -make_dot_graph_from_xml_state (const xml::document &xml_state) +diagnostics::state_graphs:: +make_dot_graph (const diagnostics::digraphs::digraph &state_graph, + const logical_location_manager &logical_loc_mgr) { - return std::make_unique (xml_state); + return std::make_unique (state_graph, logical_loc_mgr); } diff --git a/gcc/diagnostic-state.h b/gcc/diagnostic-state.h deleted file mode 100644 index 68c3e00266e2..000000000000 --- a/gcc/diagnostic-state.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Capturing changing state in diagnostic paths. - Copyright (C) 2025 Free Software Foundation, Inc. - Contributed by David Malcolm . - -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 -. */ - -#ifndef GCC_DIAGNOSTIC_STATE_H -#define GCC_DIAGNOSTIC_STATE_H - -/* We want to be able to express changing program states in diagnostic paths, - so that we can emit this in HTML and SARIF output, and to keep this - separate from implementation details of -fanalyzer. - - For now, we use xml::document as the type in the diagnostic subsystem - for (optionally) tracking the state at a diagnostic_event. */ - -namespace xml { class document; } -namespace dot { class graph; } - -extern std::unique_ptr -make_dot_graph_from_xml_state (const xml::document &xml_state); - -#endif /* GCC_DIAGNOSTIC_STATE_H */ diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 088390bc4662..7e5ac878cfea 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -1624,6 +1624,14 @@ diagnostic_context::report_verbatim (text_info &text) } } +void +diagnostic_context:: +report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + for (auto sink : m_output_sinks) + sink->report_global_digraph (ldg); +} + /* Get the number of digits in the decimal representation of VALUE. */ int diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 00f6e352d401..04eb2b116567 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -26,6 +26,12 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "diagnostic-core.h" +namespace diagnostics { + namespace digraphs { + class lazy_digraph; + } // namespace digraphs +} // namespace diagnostics + namespace text_art { class theme; @@ -616,6 +622,11 @@ public: bool report_diagnostic (diagnostic_info *); void report_verbatim (text_info &); + /* Report a directed graph associated with the run as a whole + to any sinks that support directed graphs. */ + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &); + diagnostic_t classify_diagnostic (diagnostic_option_id option_id, diagnostic_t new_kind, diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index 4dd14c8895cf..4efba4d157a0 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -624,17 +624,17 @@ which writes out the diagnostics in HTML form, and generates SVG state diagrams visualizing the state of memory at each event (inspired by the "ddd" debugger). These can be seen by pressing 'j' and 'k' to single-step forward and backward through events. Note that these SVG diagrams are -created from an intermediate XML representation generated from -@code{program_state} objects. The XML representation can be easier to +created from an intermediate SARIF directed graph representation generated from +@code{program_state} objects. The SARIF representation can be easier to read - for example, rather than storing the contents of memory via byte offsets, it uses fields for structs and element indexes for arrays, recursively. However it is a different representation, and thus bugs could be hidden by this transformation. Generating the SVG diagrams requires an invocation of "dot" per event, so it noticeably slows down diagnostic -emission, hence the opt-in command-line flag. The XML and ``dot'' +emission, hence the opt-in command-line flag. The SARIF and ``dot'' representations can be seen by @code{__analyzer_dump_xml} and @code{__analyzer_dump_dot} below (writing them to stderr), or by adding -@code{show-state-diagrams-xml=yes} and +@code{show-state-diagrams-sarif=yes} and @code{show-state-diagrams-dot-src=yes} to the html sink, which shows them within the generated HTML next to the generated SVG. @@ -792,9 +792,9 @@ will emit a warning describing the state of the 2nd argument a name matching the 1st argument (which must be a string literal). This is for use when debugging, and may be of use in DejaGnu tests. -@item __analyzer_dump_xml +@item __analyzer_dump_sarif @smallexample -__analyzer_dump_xml (); +__analyzer_dump_sarif (); @end smallexample will dump the copious information about the analyzer's state each time it diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d49f291e0c15..9a1aa377fa55 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -6143,10 +6143,10 @@ end-users, and subject to change or removal without notice: @table @gcctabopt -@item xml-state=@r{[}yes@r{|}no@r{]} +@item state-graphs=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{xml-state=yes}, then attempt to capture detailed state information -from @option{-fanalyzer} in the generated SARIF. +If @code{state-graphs=yes}, then attempt to capture detailed state +information from @option{-fanalyzer} in the generated SARIF. @end table @@ -6194,11 +6194,11 @@ then if @code{show-state-diagrams=yes}, the generated state diagrams will also show the .dot source input to GraphViz used for the diagram. -@item show-state-diagrams-xml=@r{[}yes@r{|}no@r{]} +@item show-state-diagrams-sarif=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{show-state-diagrams-xml=yes} +If @code{show-state-diagrams-sarif=yes} then if @code{show-state-diagrams=yes}, the generated state diagrams will -also show an XML representation of the state. +also show a SARIF representation of the state. @end table diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst index 6d8c92f06ea2..0e078d0120e5 100644 --- a/gcc/doc/libgdiagnostics/topics/compatibility.rst +++ b/gcc/doc/libgdiagnostics/topics/compatibility.rst @@ -188,3 +188,34 @@ supporting command-line options and SARIF playback: * :func:`diagnostic_manager_add_sink_from_spec` * :func:`diagnostic_manager_set_analysis_target` + +.. _LIBGDIAGNOSTICS_ABI_3: + +``LIBGDIAGNOSTICS_ABI_3`` +------------------------- +``LIBGDIAGNOSTICS_ABI_3`` covers the addition of these functions for +working with directed graphs: + + * :func:`diagnostic_manager_new_graph` + + * :func:`diagnostic_manager_take_global_graph` + + * :func:`diagnostic_take_graph` + + * :func:`diagnostic_graph_release` + + * :func:`diagnostic_graph_set_description` + + * :func:`diagnostic_graph_add_node` + + * :func:`diagnostic_graph_add_edge` + + * :func:`diagnostic_graph_get_node_by_id` + + * :func:`diagnostic_graph_get_edge_by_id` + + * :func:`diagnostic_node_set_label` + + * :func:`diagnostic_node_set_location` + + * :func:`diagnostic_node_set_logical_location` diff --git a/gcc/doc/libgdiagnostics/topics/graphs.rst b/gcc/doc/libgdiagnostics/topics/graphs.rst new file mode 100644 index 000000000000..b976013cb578 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/graphs.rst @@ -0,0 +1,197 @@ +.. Copyright (C) 2025 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + This 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see + . + +.. default-domain:: c + +Graphs +====== + +.. type:: diagnostic_graph + +SARIF has support for capturing directed graphs (such as callgraphs +and control flow graphs), both at the level of the run as a whole, +and at the level of individual results. + +libgdiagnostics supports this with the following entrypoints, allowing +directed graphs to be + +* created (with :func:`diagnostic_manager_new_graph`) + +* reported "globally" (with :func:`diagnostic_manager_take_global_graph`) + +* reported as part of a :type:`diagnostic` (with :func:`diagnostic_take_graph`), or + +* discarded (with :func:`diagnostic_graph_release`). + +.. function:: diagnostic_graph * diagnostic_manager_new_graph (diagnostic_manager *manager) + + Create a new directed graph. + + The resulting graph is owned by the caller and must have one of + :func:`diagnostic_manager_take_global_graph`, + :func:`diagnostic_take_graph`, + or :func:`diagnostic_graph_release` called on it to avoid leaks. + + The parameter ``manager`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_manager_take_global_graph (diagnostic_manager *manager, \ + diagnostic_graph *graph) + + Report this graph "globally", taking ownership of it. + This won't appear in text sinks, but in SARIF sinks the graph will be + added to theRun.graphs (SARIF v2.1.0 3.14.20). + + Parameters ``manager`` and ``graph`` must both be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_take_graph (diagnostic *diag, \ + diagnostic_graph *graph) + + Add this graph to ``diag``, transferring ownership of it to ``diag``. + This won't appear in text sinks, but in SARIF sinks the graph will be + added to theResult.graphs (SARIF v2.1.0 3.27.19). + + Parameters ``diag`` and ``graph`` must both be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_graph_release (diagnostic_graph *graph) + + Release ``graph`` which must still be owned by the caller + i.e. it must *not* have had + :func:`diagnostic_manager_take_global_graph` or + :func:`diagnostic_take_graph` called on it. + + Parameters ``graph`` can be null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + + +.. function:: void diagnostic_graph_set_description (diagnostic_graph *graph, \ + const char *description) + + Set the description of ``graph`` for use in the value of the + SARIF ``description`` property (SARIF v2.1.0 section 3.39.2). + + The parameter ``graph`` must be non-null. + The parameter ``description`` can be null, for clearing any existing + description. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_node * diagnostic_graph_add_node (diagnostic_graph *graph, \ + const char *node_id, \ + diagnostic_node *parent_node) + + Create and add a new node within ``graph`` with the given `id``. + The id must be unique within nodes in ``graph``. + + The parameters ``graph`` and ``id`` must be non-null. + + ``parent_node`` can be NULL (for a top-level node in the graph), + or non-null for a child node, allowing for arbitrary nesting of + nodes. + + The new node is owned by ``graph``. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_edge * diagnostic_graph_add_edge (diagnostic_graph *graph, \ + const char *edge_id, \ + diagnostic_node *src_node, \ + diagnostic_node *dst_node, \ + const char *label) + + Create and add a new edge within ``graph``. + + The parameters ``graph``, ``src_node`` and ``dest_node`` + must be non-null. + + If non-null, then ``edge_id`` must be unique within ``graph``; + if ``edge_id`` is null then a unique id of the form "edge0", "edge1", + etc will be used automatically. + + If non-null, then ``label`` will be used for the value of the + SARIF ``label`` property (SARIF v2.1.0 section 3.41.3). + + The new edge is owned by ``graph``. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_node *diagnostic_graph_get_node_by_id (diagnostic_graph *graph, \ + const char *node_id) + + Get the node in ``graph`` with the given id, or null. + + The parameters ``graph`` and ``node_id`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_edge *diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, \ + const char *edge_id) + + Get the edge in ``graph`` with the given id, or null. + + The parameters ``graph`` and ``edge_id`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + + +.. type:: diagnostic_node + +.. function:: void diagnostic_node_set_label (diagnostic_node *node, \ + const char *label) + + Set the label of ``node`` for use in the value of the + SARIF ``label`` property (SARIF v2.1.0 section 3.40.3). + + The parameter ``node`` must be non-null. + The parameter ``label`` can be null, for clearing any existing + label. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_node_set_location (diagnostic_node *node, \ + const diagnostic_physical_location *loc) + + Set the physical location of ``node``, if any. + + The parameter ``node`` must be non-null. + The parameter ``loc`` can be null, for clearing any existing + location. + + If set, the value will be used by SARIF sinks within the + ``location`` property (SARIF v2.1.0 section 3.40.4). + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_node_set_logical_location (diagnostic_node *node, \ + const diagnostic_logical_location *logical_loc) + + Set the logical location of ``node``, if any. + + The parameter ``node`` must be non-null. + The parameter ``logical_loc`` _can be null, for clearing any existing + location. + + If set, the value will be used by SARIF sinks within the + ``location`` property (SARIF v2.1.0 section 3.40.4). + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. diff --git a/gcc/doc/libgdiagnostics/topics/index.rst b/gcc/doc/libgdiagnostics/topics/index.rst index 6eb3ed646b09..966f5ef4e350 100644 --- a/gcc/doc/libgdiagnostics/topics/index.rst +++ b/gcc/doc/libgdiagnostics/topics/index.rst @@ -35,5 +35,6 @@ Topic reference execution-paths.rst text-output.rst sarif.rst + graphs.rst ux.rst compatibility.rst diff --git a/gcc/graphviz.h b/gcc/graphviz.h index 5943589f947f..9a0fe6f55d3a 100644 --- a/gcc/graphviz.h +++ b/gcc/graphviz.h @@ -311,6 +311,16 @@ struct node_id : public ast_node m_port = std::make_unique (*other.m_port); } + node_id &operator= (const node_id &other) + { + m_id = other.m_id; + if (other.m_port) + m_port = std::make_unique (*other.m_port); + else + m_port = nullptr; + return *this; + } + void print (writer &w) const final override; id m_id; diff --git a/gcc/json.h b/gcc/json.h index 156c086d2cfd..c706f2a4fe9f 100644 --- a/gcc/json.h +++ b/gcc/json.h @@ -130,6 +130,7 @@ class value void DEBUG_FUNCTION dump () const; virtual object *dyn_cast_object () { return nullptr; } + virtual string *dyn_cast_string () { return nullptr; } static int compare (const json::value &val_a, const json::value &val_b); @@ -184,6 +185,9 @@ class object : public value static int compare (const json::object &obj_a, const json::object &obj_b); + size_t get_num_keys () const { return m_keys.length (); } + const char *get_key (size_t i) const { return m_keys[i]; } + std::unique_ptr clone_as_object () const; private: @@ -284,6 +288,7 @@ class string : public value enum kind get_kind () const final override { return JSON_STRING; } void print (pretty_printer *pp, bool formatted) const final override; std::unique_ptr clone () const final override; + string *dyn_cast_string () final override { return this; } const char *get_string () const { return m_utf8; } size_t get_length () const { return m_len; } diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h index 93b8f90aefbe..4beee446d8b4 100644 --- a/gcc/libgdiagnostics++.h +++ b/gcc/libgdiagnostics++.h @@ -34,6 +34,9 @@ class execution_path; class group; class manager; class diagnostic; +class graph; +class node; +class edge; /* Wrapper around a borrowed diagnostic_text_sink *. */ @@ -245,6 +248,9 @@ public: void take_execution_path (execution_path path); + void + take_graph (graph g); + void finish (const char *fmt, ...) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) @@ -395,10 +401,99 @@ public: void set_analysis_target (file f); + void + take_global_graph (graph g); + diagnostic_manager *m_inner; bool m_owned; }; +class graph +{ +public: + graph () : m_inner (nullptr), m_owned (false) {} + + graph (diagnostic_graph *graph) + : m_inner (graph), m_owned (true) + {} + + graph (const diagnostic_graph *graph) + : m_inner (const_cast (graph)), + m_owned (false) + {} + + graph (const graph &other) = delete; + graph &operator= (const graph &other) = delete; + + graph (graph &&other) + : m_inner (other.m_inner), + m_owned (other.m_owned) + { + other.m_inner = nullptr; + other.m_owned = false; + } + + graph &operator= (graph &&other) + { + m_inner = other.m_inner; + m_owned = other.m_owned; + other.m_inner = nullptr; + other.m_owned = false; + return *this; + } + + ~graph () + { + if (m_owned) + diagnostic_graph_release (m_inner); + } + + void + set_description (const char *); + + node + get_node_by_id (const char *id) const; + + edge + get_edge_by_id (const char *id) const; + + edge + add_edge (const char *id, node src_node, node dst_node, const char *label); + + diagnostic_graph *m_inner; + bool m_owned; +}; + +// Borrowed pointer to a diagnostic_node. + +class node +{ +public: + node () : m_inner (nullptr) {} + node (diagnostic_node *node_) : m_inner (node_) {} + + void + set_label (const char *); + + void + set_location (physical_location loc); + + void + set_logical_location (logical_location loc); + + diagnostic_node *m_inner; +}; + +// Borrowed edge to a diagnostic_edge. + +class edge +{ +public: + edge (diagnostic_edge *edge_) : m_inner (edge_) {} + + diagnostic_edge *m_inner; +}; + // Implementation // class file @@ -592,6 +687,14 @@ diagnostic::take_execution_path (execution_path path) path.m_owned = false; } +inline void +diagnostic::take_graph (graph g) +{ + diagnostic_take_graph (m_inner, + g.m_inner); + g.m_owned = false; +} + inline void diagnostic::finish (const char *fmt, ...) { @@ -702,6 +805,66 @@ manager::set_analysis_target (file f) diagnostic_manager_set_analysis_target (m_inner, f.m_inner); } +inline void +manager::take_global_graph (graph g) +{ + diagnostic_manager_take_global_graph (m_inner, + g.m_inner); + g.m_owned = false; +} + +// class graph + +inline void +graph::set_description (const char *desc) +{ + diagnostic_graph_set_description (m_inner, desc); +} + +inline node +graph::get_node_by_id (const char *id) const +{ + return node (diagnostic_graph_get_node_by_id (m_inner, id)); +} + +inline edge +graph::get_edge_by_id (const char *id) const +{ + return edge (diagnostic_graph_get_edge_by_id (m_inner, id)); +} + +inline edge +graph::add_edge (const char *id, + node src_node, node dst_node, + const char *label) +{ + return edge (diagnostic_graph_add_edge (m_inner, + id, + src_node.m_inner, + dst_node.m_inner, + label)); +} + +// class node + +inline void +node::set_label (const char *label) +{ + diagnostic_node_set_label (m_inner, label); +} + +inline void +node::set_location (physical_location loc) +{ + diagnostic_node_set_location (m_inner, loc.m_inner); +} + +inline void +node::set_logical_location (logical_location loc) +{ + diagnostic_node_set_logical_location (m_inner, loc.m_inner); +} + } // namespace libgdiagnostics #endif // #ifndef LIBGDIAGNOSTICSPP_H diff --git a/gcc/libgdiagnostics-private.h b/gcc/libgdiagnostics-private.h new file mode 100644 index 000000000000..0f628e429f93 --- /dev/null +++ b/gcc/libgdiagnostics-private.h @@ -0,0 +1,62 @@ +/* Private API entrypoints to libgdiagnostics purely for use by sarif-replay. + Copyright (C) 2025 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 +. */ + +#ifndef LIBGDIAGNOSTICS_PRIVATE_H +#define LIBGDIAGNOSTICS_PRIVATE_H + +#include "libgdiagnostics.h" + +namespace json { class object; } + +extern "C" { + +/* Private entrypoints, for use only by sarif-replay. + These are subject to removal without notice. */ + +/* Entrypoints added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_event_id +private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + diagnostic_graph *state_graph, + const char *fmt, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (6) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (6, 7); + +extern void +private_diagnostic_graph_set_property_bag (diagnostic_graph &graph, + std::unique_ptr properties); + +extern void +private_diagnostic_node_set_property_bag (diagnostic_node &node, + std::unique_ptr properties); + +extern void +private_diagnostic_edge_set_property_bag (diagnostic_edge &edge, + std::unique_ptr properties); + +} // extern "C" + +#endif /* LIBGDIAGNOSTICS_PRIVATE_H */ diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index 74814c7ef28a..04812dce4fe6 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MAP +#define INCLUDE_STRING #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" @@ -32,9 +33,12 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-format-sarif.h" #include "diagnostic-format-text.h" #include "diagnostic-output-spec.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "logical-location.h" #include "edit-context.h" #include "libgdiagnostics.h" +#include "libgdiagnostics-private.h" class owned_nullable_string { @@ -644,6 +648,9 @@ public: return m_prev_diag_logical_loc; } + void + take_global_graph (std::unique_ptr graph); + private: void ensure_linemap_for_file_and_line (const diagnostic_file *file, @@ -743,17 +750,53 @@ private: owned_nullable_string m_url; }; +struct diagnostic_graph : public diagnostics::digraphs::digraph +{ + diagnostic_graph (diagnostic_manager &) {} + + diagnostic_node * + add_node_with_id (std::string id, + diagnostic_node *parent_node); + diagnostic_edge * + add_edge_with_label (const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node, + const char *label); +}; + +struct diagnostic_node : public diagnostics::digraphs::node +{ + diagnostic_node (diagnostic_graph &g, + std::string id) + : node (g, std::move (id)) + { + } +}; + +struct diagnostic_edge : public diagnostics::digraphs::edge +{ + diagnostic_edge (diagnostic_graph &g, + const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node) + : edge (g, id, src_node, dst_node) + { + } +}; + class libgdiagnostics_path_event : public diagnostic_event { public: libgdiagnostics_path_event (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, + std::unique_ptr state_graph, const char *gmsgid, va_list *args) : m_physical_loc (physical_loc), m_logical_loc (logical_loc), - m_stack_depth (stack_depth) + m_stack_depth (stack_depth), + m_state_graph (std::move (state_graph)) { m_desc_uncolored = make_desc (gmsgid, args, false); m_desc_colored = make_desc (gmsgid, args, true); @@ -799,6 +842,15 @@ public: return 0; } + std::unique_ptr + maybe_make_diagnostic_state_graph (bool) const final override + { + if (!m_state_graph) + return nullptr; + + return m_state_graph->clone (); + } + private: static label_text make_desc (const char *gmsgid, va_list *args, @@ -824,6 +876,7 @@ private: const diagnostic_physical_location *m_physical_loc; const diagnostic_logical_location *m_logical_loc; unsigned m_stack_depth; + std::unique_ptr m_state_graph; label_text m_desc_uncolored; label_text m_desc_colored; }; @@ -855,6 +908,7 @@ struct diagnostic_execution_path : public diagnostic_path add_event_va (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, + std::unique_ptr state_graph, const char *gmsgid, va_list *args) { @@ -862,6 +916,7 @@ struct diagnostic_execution_path : public diagnostic_path (std::make_unique (physical_loc, logical_loc, stack_depth, + std::move (state_graph), gmsgid, args)); return m_events.size () - 1; @@ -902,6 +957,27 @@ private: std::vector> m_events; }; +class prebuilt_digraphs : public diagnostics::digraphs::lazy_digraphs +{ +public: + using digraph = diagnostics::digraphs::digraph; + + std::unique_ptr>> + create_digraphs () const final override + { + return std::make_unique>> (std::move (m_digraphs)); + } + + void + take_graph (std::unique_ptr graph) + { + m_digraphs.push_back (std::move (graph)); + } + +private: + mutable std::vector> m_digraphs; +}; + /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic @@ -914,7 +990,9 @@ public: m_rich_loc (diag_mgr.get_line_table ()), m_logical_loc (nullptr), m_path (nullptr) - {} + { + m_metadata.set_lazy_digraphs (&m_graphs); + } diagnostic_manager &get_manager () const { @@ -990,12 +1068,25 @@ public: m_rich_loc.set_path (path); } + void + take_graph (std::unique_ptr graph) + { + m_graphs.take_graph (std::move (graph)); + } + + const prebuilt_digraphs & + get_graphs () const + { + return m_graphs; + } + private: diagnostic_manager &m_diag_mgr; enum diagnostic_level m_level; impl_rich_location m_rich_loc; const diagnostic_logical_location *m_logical_loc; diagnostic_metadata m_metadata; + prebuilt_digraphs m_graphs; std::vector> m_labels; std::vector> m_rules; std::unique_ptr m_path; @@ -1248,6 +1339,29 @@ diagnostic_manager::new_execution_path () return new diagnostic_execution_path (*mgr); } +void +diagnostic_manager::take_global_graph (std::unique_ptr graph) +{ + class prebuilt_lazy_digraph : public diagnostics::digraphs::lazy_digraph + { + public: + prebuilt_lazy_digraph (std::unique_ptr graph) + : m_graph (std::move (graph)) + { + } + + std::unique_ptr + create_digraph () const final override + { + return std::move (m_graph); + } + + private: + mutable std::unique_ptr m_graph; + }; + + m_dc.report_global_digraph (prebuilt_lazy_digraph (std::move (graph))); +} /* Error-checking at the API boundary. */ #define FAIL_IF_NULL(PTR_ARG) \ @@ -1876,6 +1990,7 @@ diagnostic_execution_path_add_event (diagnostic_execution_path *path, diagnostic_event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, + nullptr, gmsgid, &args); va_end (args); @@ -1898,6 +2013,7 @@ diagnostic_execution_path_add_event_va (diagnostic_execution_path *path, diagnostic_event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, + nullptr, gmsgid, args); return as_diagnostic_event_id (result); } @@ -2048,3 +2164,223 @@ diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, mgr->get_dc ().set_main_input_filename (file->get_name ()); } + +// struct diagnostic_graph : public diagnostics::digraphs::graph + +diagnostic_node * +diagnostic_graph::add_node_with_id (std::string id, + diagnostic_node *parent_node) +{ + auto node_up = std::make_unique (*this, std::move (id)); + diagnostic_node *new_node = node_up.get (); + if (parent_node) + parent_node->add_child (std::move (node_up)); + else + add_node (std::move (node_up)); + return new_node; +} + +diagnostic_edge * +diagnostic_graph::add_edge_with_label (const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node, + const char *label) +{ + auto edge_up + = std::make_unique (*this, id, + src_node, dst_node); + diagnostic_edge *new_edge = edge_up.get (); + if (label) + new_edge->set_label (label); + add_edge (std::move (edge_up)); + return new_edge; +} + +/* Public entrypoint. */ + +diagnostic_graph * +diagnostic_manager_new_graph (diagnostic_manager *manager) +{ + FAIL_IF_NULL (manager); + + return new diagnostic_graph (*manager); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_take_global_graph (diagnostic_manager *manager, + diagnostic_graph *graph) +{ + FAIL_IF_NULL (manager); + FAIL_IF_NULL (graph); + + manager->take_global_graph (std::unique_ptr (graph)); +} + +void +diagnostic_take_graph (diagnostic *diag, + diagnostic_graph *graph) +{ + FAIL_IF_NULL (diag); + FAIL_IF_NULL (graph); + + diag->take_graph (std::unique_ptr (graph)); +} + +/* Public entrypoint. */ + +void +diagnostic_graph_release (diagnostic_graph *graph) +{ + delete graph; +} + +/* Public entrypoint. */ + +void +diagnostic_graph_set_description (diagnostic_graph *graph, + const char *desc) +{ + FAIL_IF_NULL (graph); + + graph->set_description (desc); +} + +diagnostic_node * +diagnostic_graph_add_node (diagnostic_graph *graph, + const char *id, + diagnostic_node *parent_node) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return graph->add_node_with_id (id, parent_node); +} + +/* Public entrypoint. */ + +diagnostic_edge * +diagnostic_graph_add_edge (diagnostic_graph *graph, + const char *id, + diagnostic_node *src_node, + diagnostic_node *dst_node, + const char *label) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (src_node); + FAIL_IF_NULL (dst_node); + + return graph->add_edge_with_label (id, *src_node, *dst_node, label); +} + +/* Public entrypoint. */ + +diagnostic_node * +diagnostic_graph_get_node_by_id (diagnostic_graph *graph, + const char *id) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return static_cast (graph->get_node_by_id (id)); +} + +/* Public entrypoint. */ + +diagnostic_edge * +diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, + const char *id) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return static_cast (graph->get_edge_by_id (id)); +} + +/* Public entrypoint. */ + +void +diagnostic_node_set_location (diagnostic_node *node, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (node); + + node->set_physical_loc (loc ? loc->m_inner : UNKNOWN_LOCATION); +} + +/* Public entrypoint. */ + +void +diagnostic_node_set_label (diagnostic_node *node, + const char *label) +{ + FAIL_IF_NULL (node); + + node->set_label (label); +} + +void +diagnostic_node_set_logical_location (diagnostic_node *node, + const diagnostic_logical_location *logical_loc) +{ + FAIL_IF_NULL (node); + + node->set_logical_loc + (impl_logical_location_manager::key_from_ptr (logical_loc)); +} + +/* Private entrypoint. */ + +diagnostic_event_id +private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + diagnostic_graph *state_graph, + const char *gmsgid, ...) + +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + va_list args; + va_start (args, gmsgid); + diagnostic_event_id_t result + = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + std::unique_ptr (state_graph), + gmsgid, &args); + va_end (args); + + return as_diagnostic_event_id (result); + +} + +/* Private entrypoint. */ + +void +private_diagnostic_graph_set_property_bag (diagnostic_graph &graph, + std::unique_ptr properties) +{ + graph.set_property_bag (std::move (properties)); +} + +/* Private entrypoint. */ + +void +private_diagnostic_node_set_property_bag (diagnostic_node &node, + std::unique_ptr properties) +{ + node.set_property_bag (std::move (properties)); +} + +/* Private entrypoint. */ + +void +private_diagnostic_edge_set_property_bag (diagnostic_edge &edge, + std::unique_ptr properties) +{ + edge.set_property_bag (std::move (properties)); +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h index 9af2747cb825..f79790a6cf7f 100644 --- a/gcc/libgdiagnostics.h +++ b/gcc/libgdiagnostics.h @@ -765,6 +765,140 @@ diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); +/* Directed graphs. */ + +typedef struct diagnostic_graph diagnostic_graph; +typedef struct diagnostic_node diagnostic_node; +typedef struct diagnostic_edge diagnostic_edge; + +/* Create a new graph. + This is owned by the caller and must have one of + diagnostic_manager_take_global_graph, diagnostic_take_graph, + or diagnostic_graph_release called on it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_graph * +diagnostic_manager_new_graph (diagnostic_manager *manager) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Report this graph "globally", taking ownership of it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_manager_take_global_graph (diagnostic_manager *manager, + diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Add this graph to DIAG, transferring ownership to it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_take_graph (diagnostic *diag, + diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Release this graph. Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_graph_release (diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (1); + +/* Set the description of GRAPH for use + in the value of the SARIF "description" property + (SARIF v2.1.0 section 3.39.2). + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_graph_set_description (diagnostic_graph *graph, + const char *description) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Create and add a new node within GRAPH. + NODE_ID must be unique within nodes in GRAPH. + The new node is owned by GRAPH. + PARENT_NODE can be NULL (for a top-level node in the graph), + or non-null for a child node. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_node * +diagnostic_graph_add_node (diagnostic_graph *graph, + const char *node_id, + diagnostic_node *parent_node) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3); + +/* Create and add a new edge within GRAPH. + + If non-null, then EDGE_ID must be unique within edges in GRAPH; + if EDGE_ID is null then a unique id of the form "edge0", "edge1", etc + will be used automatically. + + The new edge is owned by GRAPH. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_edge * +diagnostic_graph_add_edge (diagnostic_graph *graph, + const char *edge_id, + diagnostic_node *src_node, + diagnostic_node *dst_node, + const char *label) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5); + +/* Get the node in GRAPH with the given id, or null. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_node * +diagnostic_graph_get_node_by_id (diagnostic_graph *graph, + const char *node_id) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Get the edge in GRAPH with the given id, or null. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_edge * +diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, + const char *edge_id) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Set the label of NODE for use + in the value of the SARIF "label" property + (SARIF v2.1.0 section 3.40.3). + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_label (diagnostic_node *node, + const char *label) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Set the physical location of NODE. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_location (diagnostic_node *node, + const diagnostic_physical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Set the logical location of NODE. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_logical_location (diagnostic_node *node, + const diagnostic_logical_location *logical_loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + /* DEFERRED: - thread-safety - plural forms diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map index 49cabcad0b7c..cae28d14425a 100644 --- a/gcc/libgdiagnostics.map +++ b/gcc/libgdiagnostics.map @@ -90,3 +90,26 @@ LIBGDIAGNOSTICS_ABI_2 { diagnostic_manager_add_sink_from_spec; diagnostic_manager_set_analysis_target; } LIBGDIAGNOSTICS_ABI_1; + +# Add hooks needed for diagnostic_graph support. +LIBGDIAGNOSTICS_ABI_3 { + global: + diagnostic_manager_new_graph; + diagnostic_manager_take_global_graph; + diagnostic_take_graph; + diagnostic_graph_release; + diagnostic_graph_set_description; + diagnostic_graph_add_node; + diagnostic_graph_add_edge; + diagnostic_graph_get_node_by_id; + diagnostic_graph_get_edge_by_id; + diagnostic_node_set_label; + diagnostic_node_set_location; + diagnostic_node_set_logical_location; + + # Private hooks used by sarif-replay + private_diagnostic_execution_path_add_event_2; + private_diagnostic_graph_set_property_bag; + private_diagnostic_node_set_property_bag; + private_diagnostic_edge_set_property_bag; +} LIBGDIAGNOSTICS_ABI_2; diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index 2d6c394e6976..cad535bc5287 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "libgdiagnostics++.h" +#include "libgdiagnostics-private.h" #include "json-parsing.h" #include "intl.h" #include "sarif-spec-urls.def" @@ -291,6 +292,8 @@ public: label_text m_label; }; +using id_map = std::map; + class sarif_replayer { public: @@ -413,6 +416,30 @@ private: const json::object &run_obj, libgdiagnostics::execution_path &out); + // "graph" object (§3.39) + enum status + handle_graph_object (const json::object &graph_obj, + const json::object &run_obj, + libgdiagnostics::graph &out); + // "node" object (§3.40) + libgdiagnostics::node + handle_node_object (const json::object &node_obj, + const json::object &run_obj, + libgdiagnostics::graph &graph, + libgdiagnostics::node parent_node, + id_map &node_id_map); + + // "edge" object (§3.41) + libgdiagnostics::edge + handle_edge_object (const json::object &edge_obj, + libgdiagnostics::graph &graph, + id_map &edge_id_map); + + libgdiagnostics::node + get_graph_node_by_id_property (const json::object &edge_json_object, + const property_spec_ref &id_prop, + libgdiagnostics::graph &graph); + // reportingDescriptor lookup (§3.52.3) const json::object * lookup_rule_by_id_in_tool (const char *rule_id, @@ -446,7 +473,7 @@ private: { va_list ap; va_start (ap, gmsgid); - report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR); + report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR); va_end (ap); return status::err_invalid_sarif; } @@ -462,14 +489,25 @@ private: { va_list ap; va_start (ap, gmsgid); - report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY); + report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY); va_end (ap); return status::err_unhandled_sarif; } + void + report_note (const json::value &jv, + const char *gmsgid, ...) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (3, 4) + { + va_list ap; + va_start (ap, gmsgid); + report_problem (jv, nullptr, gmsgid, &ap, DIAGNOSTIC_LEVEL_NOTE); + va_end (ap); + } + void report_problem (const json::value &jv, - const spec_ref &ref, + const spec_ref *ref, const char *gmsgid, va_list *args, enum diagnostic_level level) @@ -481,11 +519,14 @@ private: There doesn't seem to be a systematic mapping from spec sections to HTML anchors, so we can't provide URLs (filed as https://github.com/oasis-tcs/sarif-spec/issues/533 ). */ - char *ref_desc = ref.make_description (); - char *ref_url = ref.make_url (); - diag.add_rule (ref_desc, ref_url); - free (ref_desc); - free (ref_url); + if (ref) + { + char *ref_desc = ref->make_description (); + char *ref_url = ref->make_url (); + diag.add_rule (ref_desc, ref_url); + free (ref_desc); + free (ref_url); + } auto loc_range = make_physical_location (m_control_mgr, @@ -651,6 +692,38 @@ private: const string_property_value *value_arr, size_t num_values); + const json::object * + maybe_get_property_bag (const json::object &obj) + { + const property_spec_ref properties + ("object", "properties", "3.8.1"); + return get_optional_property (obj, properties); + } + + /* Look for a property bag within OBJ. + If found, look for a property within it named PROPERTY_NAME + of the given type. + If successful, return the property's value. + Otherwise, return nullptr without complaining (unless the property bag + is itself not an object). */ + template + const JsonType * + maybe_get_property_bag_value (const json::object &obj, + const char *property_name) + { + auto property_bag_obj = maybe_get_property_bag (obj); + if (!property_bag_obj) + return nullptr; + const json::value *property_val = property_bag_obj->get (property_name); + if (!property_val) + return nullptr; + const JsonType *sub = dyn_cast (property_val); + if (!sub) + /* Property is wrong kind of value. Don't treat this as an error. */ + return nullptr; + return sub; + } + /* The manager to replay the SARIF files to. */ libgdiagnostics::manager m_output_mgr; @@ -929,6 +1002,31 @@ sarif_replayer::handle_run_obj (const json::object &run_obj) break; } + // §3.14.20 "graphs" + const property_spec_ref prop_graphs ("run", "graphs", "3.14.20"); + if (const json::array *graphs + = get_optional_property (run_obj, + prop_graphs)) + { + for (auto element : *graphs) + { + if (const json::object *graph_json_obj + = require_object_for_element (*element, prop_graphs)) + { + libgdiagnostics::graph graph; + enum status s = handle_graph_object (*graph_json_obj, + run_obj, + graph); + if (s != status::ok) + return s; + + m_output_mgr.take_global_graph (std::move (graph)); + } + else + return status::err_invalid_sarif; + } + } + return status::ok; } @@ -1244,6 +1342,31 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, if (path.m_inner) err.take_execution_path (std::move (path)); + // §3.27.19 "graphs" property + const property_spec_ref prop_graphs ("result", "graphs", "3.27.19"); + if (const json::array *graphs + = get_optional_property (result_obj, + prop_graphs)) + { + for (auto element : *graphs) + { + if (const json::object *graph_json_obj + = require_object_for_element (*element, prop_graphs)) + { + libgdiagnostics::graph graph; + enum status s = handle_graph_object (*graph_json_obj, + run_obj, + graph); + if (s != status::ok) + return s; + + err.take_graph (std::move (graph)); + } + else + return status::err_invalid_sarif; + } + } + // §3.27.22 relatedLocations property std::vector> notes; const property_spec_ref prop_related_locations @@ -1740,16 +1863,32 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj, } } + libgdiagnostics::graph state_graph; + if (auto sarif_state_graph + = maybe_get_property_bag_value (tflow_loc_obj, + "gcc/diagnostic_event/state_graph")) + { + enum status s + = handle_graph_object (*sarif_state_graph, run_obj, state_graph); + if (s != status::ok) + return s; + } + if (message.get ()) - path.add_event (physical_loc, - logical_loc, - stack_depth, - "%s", message.get ()); + private_diagnostic_execution_path_add_event_2 (path.m_inner, + physical_loc.m_inner, + logical_loc.m_inner, + stack_depth, + state_graph.m_inner, + "%s", message.get ()); else - path.add_event (physical_loc, - logical_loc, - stack_depth, - ""); + private_diagnostic_execution_path_add_event_2 (path.m_inner, + physical_loc.m_inner, + logical_loc.m_inner, + stack_depth, + state_graph.m_inner, + ""); + state_graph.m_owned = false; return status::ok; } @@ -2142,6 +2281,270 @@ handle_logical_location_object (const json::object &logical_loc_obj, return status::ok; } +// "graph" object (§3.39) + +enum status +sarif_replayer::handle_graph_object (const json::object &graph_json_obj, + const json::object &run_obj, + libgdiagnostics::graph &out_graph) +{ + out_graph = libgdiagnostics::graph + (diagnostic_manager_new_graph (m_output_mgr.m_inner)); + + id_map node_id_map; + id_map edge_id_map; + + if (auto properties = maybe_get_property_bag (graph_json_obj)) + private_diagnostic_graph_set_property_bag (*out_graph.m_inner, + properties->clone_as_object ()); + + // §3.39.2: MAY contain a "description" property + const property_spec_ref description_prop + ("graph", "description", "3.39.2"); + if (auto description_obj + = get_optional_property (graph_json_obj, description_prop)) + { + label_text text + = make_plain_text_within_result_message (&run_obj, + *description_obj, + nullptr); + if (!text.get ()) + return status::err_invalid_sarif; + out_graph.set_description (text.get ()); + } + + // §3.39.3: MAY contain a "nodes" property + const property_spec_ref nodes_prop + ("graph", "nodes", "3.39.3"); + if (auto nodes_arr + = get_optional_property (graph_json_obj, nodes_prop)) + { + for (auto element : *nodes_arr) + { + const json::object *node_json_obj + = require_object_for_element (*element, nodes_prop); + if (!node_json_obj) + return status::err_invalid_sarif; + libgdiagnostics::node node + = handle_node_object (*node_json_obj, run_obj, out_graph, + nullptr, node_id_map); + if (node.m_inner == nullptr) + return status::err_invalid_sarif; + } + } + else + // If we have no nodes, we can't handle the edges. + return status::ok; + + // §3.39.4 edges property + const property_spec_ref edges_prop + ("graph", "edges", "3.39.4"); + if (auto edges_arr + = get_optional_property (graph_json_obj, edges_prop)) + { + for (auto element : *edges_arr) + { + const json::object *edge_json_obj + = require_object_for_element (*element, edges_prop); + if (!edge_json_obj) + return status::err_invalid_sarif; + libgdiagnostics::edge edge + = handle_edge_object (*edge_json_obj, out_graph, edge_id_map); + if (edge.m_inner == nullptr) + return status::err_invalid_sarif; + } + } + + return status::ok; +} + +// "node" object (§3.40) + +libgdiagnostics::node +sarif_replayer::handle_node_object (const json::object &node_json_obj, + const json::object &run_obj, + libgdiagnostics::graph &graph, + libgdiagnostics::node parent_node, + id_map &node_id_map) +{ + // §3.40.2 "id" property + const property_spec_ref id_prop ("node", "id", "3.40.2"); + auto id_str = get_required_property (node_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + if (diagnostic_graph_get_node_by_id (graph.m_inner, id)) + { + // Duplicate id; fail: + libgdiagnostics::group g (m_control_mgr); + report_invalid_sarif (*id_str, + id_prop, + "duplicate node id %qs within graph", + id); + gcc_assert (node_id_map[id]); + report_note (*node_id_map[id], + "%qs already used as node id within graph here", + id); + return nullptr; + } + node_id_map[id] = id_str; + + libgdiagnostics::node new_node + = libgdiagnostics::node (diagnostic_graph_add_node (graph.m_inner, + id, + parent_node.m_inner)); + if (auto properties = maybe_get_property_bag (node_json_obj)) + private_diagnostic_node_set_property_bag (*new_node.m_inner, + properties->clone_as_object ()); + + // §3.40.3 "label" property + const property_spec_ref label_prop + ("node", "label", "3.40.3"); + if (auto label_obj + = get_optional_property (node_json_obj, label_prop)) + { + label_text text + = make_plain_text_within_result_message (&run_obj, + *label_obj, + nullptr); + if (!text.get ()) + return nullptr; + new_node.set_label (text.get ()); + } + + // §3.40.4 "location" property + const property_spec_ref location_prop ("node", "location", "3.40.4"); + if (auto location_json_obj + = get_optional_property (node_json_obj, location_prop)) + { + libgdiagnostics::physical_location physical_loc; + libgdiagnostics::logical_location logical_loc; + enum status s = handle_location_object (*location_json_obj, + run_obj, + physical_loc, + logical_loc, + nullptr); // annotations + if (s != status::ok) + return nullptr; + + new_node.set_location (physical_loc); + new_node.set_logical_location (logical_loc); + } + + // §3.40.5: MAY contain a "children" property + const property_spec_ref children_prop + ("graph", "children", "3.40.5"); + if (auto children_json_arr + = get_optional_property (node_json_obj, children_prop)) + { + for (auto json_element : *children_json_arr) + { + const json::object *child_json_obj + = require_object_for_element (*json_element, children_prop); + if (!child_json_obj) + return nullptr; + libgdiagnostics::node child_node + = handle_node_object (*child_json_obj, run_obj, graph, + new_node, node_id_map); + if (child_node.m_inner == nullptr) + return nullptr; + } + } + + return new_node; +} + +// "edge" object (§3.41) + +libgdiagnostics::edge +sarif_replayer::handle_edge_object (const json::object &edge_json_obj, + libgdiagnostics::graph &graph, + id_map &edge_id_map) +{ + // §3.41.2 "id" property + const property_spec_ref id_prop ("edge", "id", "3.41.2"); + auto id_str = get_required_property (edge_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + if (diagnostic_graph_get_edge_by_id (graph.m_inner, id)) + { + // Duplicate id; fail: + libgdiagnostics::group g (m_control_mgr); + report_invalid_sarif (*id_str, + id_prop, + "duplicate edge id %qs within graph", + id); + gcc_assert (edge_id_map[id]); + report_note (*edge_id_map[id], + "%qs already used as edge id within graph here", + id); + return nullptr; + } + edge_id_map[id] = id_str; + + // §3.41.3 "label" property + label_text label; + const property_spec_ref label_prop + ("edge", "label", "3.41.3"); + if (auto label_obj + = get_optional_property (edge_json_obj, label_prop)) + { + label = make_plain_text_within_result_message (nullptr, + *label_obj, + nullptr); + if (!label.get ()) + return nullptr; + } + + // §3.41.4 "sourceNodeId" property + const property_spec_ref src_id_prop ("edge", "sourceNodeId", "3.41.4"); + auto src_node = get_graph_node_by_id_property (edge_json_obj, + src_id_prop, + graph); + if (!src_node.m_inner) + return nullptr; + + // §3.41.5 "targetNodeId" property + const property_spec_ref dst_id_prop ("edge", "targetNodeId", "3.41.5"); + auto dst_node = get_graph_node_by_id_property (edge_json_obj, + dst_id_prop, + graph); + if (!dst_node.m_inner) + return nullptr; + + auto result = graph.add_edge (id, src_node, dst_node, label.get ()); + + if (auto properties = maybe_get_property_bag (edge_json_obj)) + private_diagnostic_edge_set_property_bag (*result.m_inner, + properties->clone_as_object ()); + + return result; +} + +libgdiagnostics::node +sarif_replayer:: +get_graph_node_by_id_property (const json::object &edge_json_obj, + const property_spec_ref &id_prop, + libgdiagnostics::graph &graph) +{ + auto id_str = get_required_property (edge_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + auto node = graph.get_node_by_id (id); + if (!node.m_inner) + { + // id not found; complain: + report_invalid_sarif (*id_str, + id_prop, + "no node with id %qs in graph", + id); + return nullptr; + } + return node; +} + // 3.52.3 reportingDescriptor lookup // "For an example of the interaction between ruleId and rule.id, see §3.52.4." diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index 324545302f79..d43d9dd65131 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -102,7 +102,9 @@ selftest::run_tests () diagnostic_show_locus_cc_tests (); diagnostic_format_html_cc_tests (); diagnostic_format_sarif_cc_tests (); + diagnostic_digraphs_cc_tests (); diagnostic_output_spec_cc_tests (); + diagnostic_state_graphs_cc_tests (); edit_context_cc_tests (); fold_const_cc_tests (); spellcheck_cc_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index eb9e604a321f..06485e1fffaf 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -222,11 +222,13 @@ extern void cgraph_cc_tests (); extern void convert_cc_tests (); extern void dbgcnt_cc_tests (); extern void diagnostic_color_cc_tests (); +extern void diagnostic_digraphs_cc_tests (); extern void diagnostic_format_html_cc_tests (); extern void diagnostic_format_sarif_cc_tests (); extern void diagnostic_output_spec_cc_tests (); extern void diagnostic_path_output_cc_tests (); extern void diagnostic_show_locus_cc_tests (); +extern void diagnostic_state_graphs_cc_tests (); extern void digraph_cc_tests (); extern void dumpfile_cc_tests (); extern void edit_context_cc_tests (); diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py index d2967d42a307..d92af8376ccc 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py @@ -6,7 +6,7 @@ import pytest def sarif(): return sarif_from_env() -def test_xml_state(sarif): +def test_state_graph(sarif): result = get_result_by_index(sarif, 0) assert result['level'] == 'warning' @@ -17,16 +17,49 @@ def test_xml_state(sarif): # Event "(1)": "entry to 'test'" (index == 0) assert events[0]['location']['message']['text'] == "entry to 'test'" - state0 = get_xml_state(events, 0) - memory_regions = state0.find('memory-regions') - assert memory_regions is not None - stack = memory_regions.find('stack') - assert stack is not None - frame = stack.find('stack-frame') - assert frame.get('function') == 'test' + state0 = get_state_graph(events, 0) + + stack = state0['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' + + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' # Final event: assert events[-1]['location']['message']['text'].startswith("use after 'free' of ") - state = get_xml_state(events, -1) - # TODO + state = get_state_graph(events, -1) + + stack = state['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' + + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' + + heap = state['nodes'][1] + assert heap['id'] == 'heap' + assert get_state_node_kind(heap) == 'heap' + + assert len(heap['children']) == 3 + heap_buffer0 = heap['children'][0] + assert heap_buffer0['id'].startswith('heap-allocated-region-') + assert get_state_node_kind(heap_buffer0) == 'dynalloc-buffer' + + globals_ = state['nodes'][2] + assert globals_['id'] == 'globals' + assert get_state_node_kind(globals_) == 'globals' + first = globals_['children'][0] + assert first['id'].startswith('decl-region-') + assert get_state_node_kind(first) == 'variable' + assert get_state_node_name(first) == 'first' + assert get_state_node_type(first) == 'struct node *' + + assert len(state['edges']) == 3 diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c index 3d853d2d10d8..3b35cfab3e0f 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c @@ -1,4 +1,4 @@ -/* { dg-additional-options "-fdiagnostics-add-output=sarif:xml-state=yes" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:state-graphs=yes" } */ #include "analyzer-decls.h" diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c index 28cf58042306..b981cf9268ad 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c @@ -1,4 +1,4 @@ -/* { dg-additional-options "-fdiagnostics-add-output=sarif:xml-state=yes" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:state-graphs=yes" } */ #include "analyzer-decls.h" diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py index 484da963ea23..3a2c6f8ceca4 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py @@ -1,5 +1,3 @@ -import xml.etree.ElementTree as ET - from sarif import * import pytest @@ -8,7 +6,7 @@ import pytest def sarif(): return sarif_from_env() -def test_nested_types_in_xml_state(sarif): +def test_nested_types_in_state_graph(sarif): result = get_result_by_index(sarif, 0) assert result['level'] == 'note' @@ -16,16 +14,17 @@ def test_nested_types_in_xml_state(sarif): events = result["codeFlows"][0]["threadFlows"][0]['locations'] assert events[0]['location']['message']['text'] == 'here' - state = get_xml_state(events, 0) - - memory_regions = state.find('memory-regions') - assert memory_regions is not None + state = get_state_graph(events, 0) - stack = memory_regions.find('stack') - assert stack is not None + stack = state['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' - frame = stack.find('stack-frame') - assert frame.get('function') == 'test' + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' # We have: # baz_arr[1].m_bars[1].m_foos[2].m_ints[1] = 42; @@ -34,40 +33,57 @@ def test_nested_types_in_xml_state(sarif): # representation to nested elements and fields. # "baz_arr": - baz_arr = frame.find("variable[@name='baz_arr']") - assert baz_arr.get('type') == 'struct baz[2]' + baz_arr = frame['children'][0] + assert get_state_node_kind(baz_arr) == 'variable' + assert get_state_node_type(baz_arr) == 'struct baz[2]' + + assert len(baz_arr['children']) == 2 + + bindings = baz_arr['children'][0] + assert bindings['id'].startswith('concrete-bindings-') + assert get_state_node_kind(bindings) == 'other' + assert get_state_node_value(bindings['children'][0]) == '(int)42' # "baz_arr[1]": - baz_arr_1 = baz_arr.find("element[@index='1']") - assert baz_arr_1.get('type') == 'struct baz' + baz_arr_1 = baz_arr['children'][1] + assert get_state_node_type(baz_arr_1) == 'struct baz' + assert get_state_node_kind(baz_arr_1) == 'element' + assert get_state_node_attr(baz_arr_1, 'index') == '1' - assert baz_arr.find("element[@index='0']") is None + assert len(baz_arr_1['children']) == 1 # "baz_arr[1].m_bars": - baz_arr_1_m_bars = baz_arr_1.find("field[@name='m_bars']") - assert baz_arr_1_m_bars.get('type') == 'struct bar[2]' + baz_arr_1_m_bars = baz_arr_1['children'][0] + assert get_state_node_name(baz_arr_1_m_bars) == 'm_bars' + assert get_state_node_type(baz_arr_1_m_bars) == 'struct bar[2]' # "baz_arr[1].m_bars[1]" - baz_arr_1_m_bars_1 = baz_arr_1_m_bars.find("element[@index='1']") - assert baz_arr_1_m_bars_1.get('type') == 'struct bar' + baz_arr_1_m_bars_1 = baz_arr_1_m_bars['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1) == 'struct bar' + assert get_state_node_kind(baz_arr_1_m_bars_1) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1, 'index') == '1' # "baz_arr[1].m_bars[1].m_foos" - baz_arr_1_m_bars_1_m_foos = baz_arr_1_m_bars_1.find("field[@name='m_foos']") - assert baz_arr_1_m_bars_1_m_foos.get('type') == 'struct foo[3]' + baz_arr_1_m_bars_1_m_foos = baz_arr_1_m_bars_1['children'][0] + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos) == 'field' + assert get_state_node_name(baz_arr_1_m_bars_1_m_foos) == 'm_foos' + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos) == 'struct foo[3]' # "baz_arr[1].m_bars[1].m_foos[2]" - baz_arr_1_m_bars_1_m_foos_2 = baz_arr_1_m_bars_1_m_foos.find("element[@index='2']") - assert baz_arr_1_m_bars_1_m_foos_2.get('type') == 'struct foo' - + baz_arr_1_m_bars_1_m_foos_2 = baz_arr_1_m_bars_1_m_foos['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2) == 'struct foo' + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1_m_foos_2, 'index') == '2' + # "baz_arr[1].m_bars[1].m_foos[2].m_ints" - baz_arr_1_m_bars_1_m_foos_2_m_ints = baz_arr_1_m_bars_1_m_foos_2.find('field[@name="m_ints"]') - assert baz_arr_1_m_bars_1_m_foos_2_m_ints.get('type') == 'int[4]' - - # "baz_arr[1].m_bars[1].m_foos[2].m_ints[1]" - baz_arr_1_m_bars_1_m_foos_2_m_ints_1 = baz_arr_1_m_bars_1_m_foos_2_m_ints.find('element[@index="1"]') - assert baz_arr_1_m_bars_1_m_foos_2_m_ints_1.get('type') == 'int' + baz_arr_1_m_bars_1_m_foos_2_m_ints = baz_arr_1_m_bars_1_m_foos_2['children'][0] + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'field' + assert get_state_node_name(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'm_ints' + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'int[4]' - value = baz_arr_1_m_bars_1_m_foos_2_m_ints_1.find('value-of-region') - constant = value.find('constant') - assert constant.get('value') == '42' - assert constant.get('type') == 'int' + # "baz_arr[1].m_bars[1].m_foos[2].m_ints[1]" + baz_arr_1_m_bars_1_m_foos_2_m_ints_1 = baz_arr_1_m_bars_1_m_foos_2_m_ints['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == 'int' + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1_m_foos_2_m_ints_1, 'index') == '1' + assert get_state_node_value(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == '(int)42' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c new file mode 100644 index 000000000000..2256a63465f1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} + +/* Use a Python script to verify various properties about the generated + HTML file: + { dg-final { run-html-pytest diagnostic-test-graphs-html.c "diagnostic-test-graphs-html.py" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py new file mode 100644 index 000000000000..11e5fd1a3859 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py @@ -0,0 +1,48 @@ +# Verify that metadata works in HTML output. + +from htmltest import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def html_tree(): + return html_tree_from_env() + +def test_result_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + diag_list = body.find('xhtml:div', ns) + assert diag_list is not None + assert diag_list.attrib['class'] == 'gcc-diagnostic-list' + + diag = diag_list.find('xhtml:div', ns) + assert diag is not None + + message = diag.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-0-message' + + assert message[0].tag == make_tag('strong') + assert message[0].tail == ' this is a placeholder error, with graphs ' + + graph = diag.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'foo' + +def test_run_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + graph = body.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'Optimization Passes' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c new file mode 100644 index 000000000000..4170f51de628 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-add-output=sarif" } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest diagnostic-test-graphs-sarif.c "diagnostic-test-graphs-sarif.py" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py new file mode 100644 index 000000000000..4bb7535cc64a --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py @@ -0,0 +1,55 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_basics(sarif): + schema = sarif['$schema'] + assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json" + + version = sarif['version'] + assert version == "2.1.0" + +def test_result_graph(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + assert len(results) == 1 + + result = results[0] + assert result['level'] == 'error' + assert result['message']['text'] == "this is a placeholder error, with graphs" + + assert len(result['graphs']) == 2 + + assert result['graphs'][0]['description']['text'] == 'foo' + + assert len(result['graphs'][0]['nodes']) == 2 + assert result['graphs'][0]['nodes'][0]['id'] == 'a' + assert result['graphs'][0]['nodes'][1]['id'] == 'b' + assert result['graphs'][0]['nodes'][1]['properties']['/placeholder-prefix/color'] == 'red' + assert len(result['graphs'][0]['nodes'][1]['children']) == 1 + assert result['graphs'][0]['nodes'][1]['children'][0]['id'] == 'c' + assert result['graphs'][0]['nodes'][1]['children'][0]['label']['text'] == 'I am a node label' + + assert len(result['graphs'][0]['edges']) == 1 + result['graphs'][0]['edges'][0]['id'] == 'my-edge' + assert result['graphs'][0]['edges'][0]['label']['text'] == 'I am an edge label' + assert result['graphs'][0]['edges'][0]['sourceNodeId'] == 'a' + assert result['graphs'][0]['edges'][0]['targetNodeId'] == 'c' + + assert result['graphs'][1]['description']['text'] == 'bar' + +def test_run_graph(sarif): + runs = sarif['runs'] + run = runs[0] + + assert len(run['graphs']) == 1 + + assert run['graphs'][0]['description']['text'] == 'Optimization Passes' + assert run['graphs'][0]['nodes'][0]['id'] == 'all_lowering_passes' + assert run['graphs'][0]['edges'][0]['id'] == 'edge0' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c new file mode 100644 index 000000000000..79739546886c --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc new file mode 100644 index 000000000000..d43216170631 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc @@ -0,0 +1,283 @@ +/* This plugin exercises diagnostic graphs. + We emit an error with a pair of digraphs associated with it, + and a global digraph showing the optimization passes. */ + +#define INCLUDE_MAP +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "gcc-plugin.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "stringpool.h" +#include "toplev.h" +#include "basic-block.h" +#include "hash-table.h" +#include "vec.h" +#include "ggc.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "tree.h" +#include "tree-pass.h" +#include "intl.h" +#include "plugin-version.h" +#include "diagnostic.h" +#include "context.h" +#include "gcc-rich-location.h" +#include "diagnostic-metadata.h" +#include "diagnostic-digraphs.h" +#include "pass_manager.h" + + +int plugin_is_GPL_compatible; + +const pass_data pass_data_test_graph_emission = +{ + GIMPLE_PASS, /* type */ + "test_graph_emission", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_test_graph_emission : public gimple_opt_pass +{ +public: + pass_test_graph_emission(gcc::context *ctxt) + : gimple_opt_pass(pass_data_test_graph_emission, ctxt) + {} + + /* opt_pass methods: */ + bool gate (function *) { return true; } + virtual unsigned int execute (function *); + +}; // class pass_test_graph_emission + +/* Determine if STMT is a call with NUM_ARGS arguments to a function + named FUNCNAME. + If so, return STMT as a gcall *. Otherwise return NULL. */ + +static gcall * +check_for_named_call (gimple *stmt, + const char *funcname, unsigned int num_args) +{ + gcc_assert (funcname); + + gcall *call = dyn_cast (stmt); + if (!call) + return NULL; + + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) + return NULL; + + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname)) + return NULL; + + if (gimple_call_num_args (call) != num_args) + { + error_at (stmt->location, "expected number of args: %i (got %i)", + num_args, gimple_call_num_args (call)); + return NULL; + } + + return call; +} + +class lazy_passes_graph : public diagnostics::digraphs::lazy_digraph +{ +public: + lazy_passes_graph (const ::gcc::pass_manager &pass_manager_) + : m_pass_manager (pass_manager_) + { + } + + std::unique_ptr + create_digraph () const final override + { + auto g = std::make_unique (); + g->set_description ("Optimization Passes"); + +#define DEF_PASS_LIST(NAME) \ + add_top_level_pass_list (*g, #NAME, m_pass_manager.NAME); + + GCC_PASS_LISTS + +#undef DEF_PASS_LIST + + return g; + } + + void + add_top_level_pass_list (diagnostics::digraphs::digraph &g, + const char *pass_list_name, + const opt_pass *p) const + { + gcc_assert (p); + auto n = std::make_unique (g, pass_list_name); + n->set_label (pass_list_name); + add_child_pass (g, *n, *p); + g.add_node (std::move (n)); + } + + diagnostics::digraphs::node & + add_child_pass (diagnostics::digraphs::digraph &g, + diagnostics::digraphs::node &parent_node, + const opt_pass &p) const + { + std::string node_label; + std::string node_id; + if (p.static_pass_number > 0 ) + { + node_label = std::to_string (p.static_pass_number) + "_" + p.name; + node_id = node_label; + } + else + { + node_label = std::string (p.name); + pretty_printer pp; + pp_printf (&pp, "%s_%p", p.name, &p); + node_id = pp_formatted_text (&pp); + } + auto n + = std::make_unique (g, + std::move (node_id)); + n->set_label (node_label.c_str ()); + diagnostics::digraphs::node &result = *n; + parent_node.add_child (std::move (n)); + + // TODO: add attrs for things like type, properties_*, etc + + if (p.sub) + { + auto &other_node = add_child_pass (g, parent_node, *p.sub); + g.add_edge (nullptr, result, other_node, "sub"); + } + + if (p.next) + { + auto &other_node = add_child_pass (g, parent_node, *p.next); + g.add_edge (nullptr, result, other_node, "next"); + } + + return result; + } + +private: + const ::gcc::pass_manager &m_pass_manager; +}; + +static void +report_diag_with_graphs (location_t loc) +{ + class my_lazy_digraphs : public diagnostics::digraphs::lazy_digraphs + { + public: + using diagnostic_graph = diagnostics::digraphs::digraph; + using diagnostic_node = diagnostics::digraphs::node; + using diagnostic_edge = diagnostics::digraphs::edge; + + std::unique_ptr>> + create_digraphs () const final override + { + auto graphs + = std::make_unique>> (); + + graphs->push_back (make_test_graph ("foo")); + graphs->push_back (make_test_graph ("bar")); + + return graphs; + } + + private: + std::unique_ptr + make_test_graph (const char *desc) const + { + auto g = std::make_unique (); + g->set_description (desc); + auto a = std::make_unique (*g, "a"); + auto b = std::make_unique (*g, "b"); +#define KEY_PREFIX "/placeholder-prefix/" + b->set_attr (KEY_PREFIX, "color", "red"); +#undef KEY_PREFIX + auto c = std::make_unique (*g, "c"); + c->set_label ("I am a node label"); + + auto e = std::make_unique (*g, "my-edge", *a, *c); + e->set_label ("I am an edge label"); + g->add_edge (std::move (e)); + + g->add_node (std::move (a)); + + b->add_child (std::move (c)); + g->add_node (std::move (b)); + + return g; + } + }; + + gcc_rich_location rich_loc (loc); + diagnostic_metadata meta; + my_lazy_digraphs ldg; + meta.set_lazy_digraphs (&ldg); + error_meta (&rich_loc, meta, + "this is a placeholder error, with graphs"); +} + +/* Exercise diagnostic graph emission. */ + +unsigned int +pass_test_graph_emission::execute (function *fun) +{ + gimple_stmt_iterator gsi; + basic_block bb; + + FOR_EACH_BB_FN (bb, fun) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + if (gcall *call = check_for_named_call (stmt, "here", 0)) + report_diag_with_graphs (gimple_location (call)); + } + + return 0; +} + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + struct register_pass_info pass_info; + const char *plugin_name = plugin_info->base_name; + int argc = plugin_info->argc; + struct plugin_argument *argv = plugin_info->argv; + + if (!plugin_default_version_check (version, &gcc_version)) + return 1; + + pass_info.pass = new pass_test_graph_emission (g); + pass_info.reference_pass_name = "ssa"; + pass_info.ref_pass_instance_number = 1; + pass_info.pos_op = PASS_POS_INSERT_AFTER; + register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &pass_info); + + gcc_assert (::g->get_passes ()); + global_dc->report_global_digraph (lazy_passes_graph (*::g->get_passes ())); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index d1d7f5dbc037..ce25c0ab3abd 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -107,6 +107,10 @@ set plugin_test_list [list \ diagnostic-test-metadata.c \ diagnostic-test-metadata-html.c \ diagnostic-test-metadata-sarif.c } \ + { diagnostic_plugin_test_graphs.cc + diagnostic-test-graphs.c \ + diagnostic-test-graphs-html.c \ + diagnostic-test-graphs-sarif.c } \ { diagnostic_plugin_test_nesting.cc \ diagnostic-test-nesting-text-plain.c \ diagnostic-test-nesting-text-indented.c \ diff --git a/gcc/testsuite/lib/sarif.py b/gcc/testsuite/lib/sarif.py index 384de2fb0154..06d05c027a43 100644 --- a/gcc/testsuite/lib/sarif.py +++ b/gcc/testsuite/lib/sarif.py @@ -29,10 +29,24 @@ def get_result_by_index(sarif, idx): results = run['results'] return results[idx] -def get_xml_state(events, event_idx): - xml_src = events[event_idx]['properties']['gcc/diagnostic_event/xml_state'] +def get_state_graph(events, event_idx): + graph = events[event_idx]['properties']['gcc/diagnostic_event/state_graph'] if 0: - print(xml_src) - xml = ET.fromstring(xml_src) - assert xml.tag == 'state-diagram' - return xml + print(graph) + assert graph is not None + return graph + +def get_state_node_attr(obj, attr_name): + return obj['properties']['gcc/diagnostic_state_node/%s' % attr_name] + +def get_state_node_kind(obj): + return get_state_node_attr(obj, 'kind') + +def get_state_node_name(obj): + return get_state_node_attr(obj, 'name') + +def get_state_node_type(obj): + return get_state_node_attr(obj, 'type') + +def get_state_node_value(obj): + return get_state_node_attr(obj, 'value') diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif new file mode 100644 index 000000000000..a4eb9d4fba68 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif @@ -0,0 +1,23 @@ +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": { "driver": { "name": "example" } }, + "results": [], + "graphs": [{"nodes": [{"id": "a", /* { dg-message "'a' already used as node id within graph here" } */ + "children": [{"id": "a"}]}], /* { dg-error "duplicate node id 'a' within graph \\\[SARIF v2.1.0 §3.40.2\\\]" } */ + "edges": []}]}]} + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/nodes/0/children/0/id': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 6 | "children": [{"id": "a"}]}], + | ^~~ + { dg-end-multiline-output "" } */ + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/nodes/0/id': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 5 | "graphs": [{"nodes": [{"id": "a", + | ^~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif new file mode 100644 index 000000000000..b483346d1442 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif @@ -0,0 +1,16 @@ +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": { "driver": { "name": "example" } }, + "results": [], + "graphs": [{"nodes": [], + "edges": [{"id": "edge0", + "sourceNodeId": "this-does-not-exist", /* { dg-error "no node with id 'this-does-not-exist' in graph \\\[SARIF v2.1.0 §3.41.4\\\]" } */ + "targetNodeId": "neither-does-this"}]}]}]} + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/edges/0/sourceNodeId': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 7 | "sourceNodeId": "this-does-not-exist", + | ^~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py new file mode 100644 index 000000000000..63b80c98738f --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py @@ -0,0 +1,46 @@ +from htmltest import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def html_tree(): + return html_tree_from_env() + +def test_result_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + diag_list = body.find('xhtml:div', ns) + assert diag_list is not None + assert diag_list.attrib['class'] == 'gcc-diagnostic-list' + + diag = diag_list.find('xhtml:div', ns) + assert diag is not None + + message = diag.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-0-message' + + assert message[0].tag == make_tag('strong') + assert message[0].tail == ' this is a placeholder error, with graphs ' + + graph = diag.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'foo' + +def test_run_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + graph = body.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'Optimization Passes' diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py new file mode 100644 index 000000000000..4bb7535cc64a --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py @@ -0,0 +1,55 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_basics(sarif): + schema = sarif['$schema'] + assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json" + + version = sarif['version'] + assert version == "2.1.0" + +def test_result_graph(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + assert len(results) == 1 + + result = results[0] + assert result['level'] == 'error' + assert result['message']['text'] == "this is a placeholder error, with graphs" + + assert len(result['graphs']) == 2 + + assert result['graphs'][0]['description']['text'] == 'foo' + + assert len(result['graphs'][0]['nodes']) == 2 + assert result['graphs'][0]['nodes'][0]['id'] == 'a' + assert result['graphs'][0]['nodes'][1]['id'] == 'b' + assert result['graphs'][0]['nodes'][1]['properties']['/placeholder-prefix/color'] == 'red' + assert len(result['graphs'][0]['nodes'][1]['children']) == 1 + assert result['graphs'][0]['nodes'][1]['children'][0]['id'] == 'c' + assert result['graphs'][0]['nodes'][1]['children'][0]['label']['text'] == 'I am a node label' + + assert len(result['graphs'][0]['edges']) == 1 + result['graphs'][0]['edges'][0]['id'] == 'my-edge' + assert result['graphs'][0]['edges'][0]['label']['text'] == 'I am an edge label' + assert result['graphs'][0]['edges'][0]['sourceNodeId'] == 'a' + assert result['graphs'][0]['edges'][0]['targetNodeId'] == 'c' + + assert result['graphs'][1]['description']['text'] == 'bar' + +def test_run_graph(sarif): + runs = sarif['runs'] + run = runs[0] + + assert len(run['graphs']) == 1 + + assert run['graphs'][0]['description']['text'] == 'Optimization Passes' + assert run['graphs'][0]['nodes'][0]['id'] == 'all_lowering_passes' + assert run['graphs'][0]['edges'][0]['id'] == 'edge0' diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif new file mode 100644 index 000000000000..da236ba02dd0 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif @@ -0,0 +1,2445 @@ +/* Test a replay of a .sarif file generated from GCC testsuite. + + The dg directives were stripped out from the generated .sarif + to avoid confusing DejaGnu for this test. */ +/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:file=graphs.sarif.html,javascript=no" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=graphs.roundtrip.sarif" } */ + +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": {"driver": {"name": "GNU C23", + "fullName": "GNU C23 (GCC) version 16.0.0 20250702 (experimental) (x86_64-pc-linux-gnu)", + "version": "16.0.0 20250702 (experimental)", + "informationUri": "https://gcc.gnu.org/gcc-16/", + "rules": []}, + "extensions": [{"name": "diagnostic_plugin_test_graphs", + "fullName": "./diagnostic_plugin_test_graphs.so"}]}, + "invocations": [{"arguments": ["/home/david/gcc-newgit-gcc16/build/gcc/cc1", + "-quiet", + "-iprefix", + "/usr/local/lib/gcc/x86_64-pc-linux-gnu/16.0.0/", + "-isystem", + "/home/david/gcc-newgit-gcc16/build/gcc/include", + "-isystem", + "/home/david/gcc-newgit-gcc16/build/gcc/include-fixed", + "-iplugindir=/home/david/gcc-newgit-gcc16/build/gcc/plugin", + "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c", + "-iplugindir=/home/david/gcc-newgit-gcc16/build/gcc/plugin", + "-quiet", + "-dumpbase", + "diagnostic-test-graphs-sarif.c", + "-dumpbase-ext", + ".c", + "-mtune=generic", + "-march=x86-64", + "-fdiagnostics-color=never", + "-fdiagnostics-urls=never", + "-fno-diagnostics-show-caret", + "-fno-diagnostics-show-line-numbers", + "-fdiagnostics-path-format=separate-events", + "-fdiagnostics-text-art-charset=none", + "-fno-diagnostics-show-event-links", + "-fplugin=./diagnostic_plugin_test_graphs.so", + "-fdiagnostics-add-output=sarif", + "-o", + "diagnostic-test-graphs-sarif.s"], + "workingDirectory": {"uri": "/home/david/gcc-newgit-gcc16/build/gcc/testsuite/gcc"}, + "startTimeUtc": "2025-07-09T22:43:31Z", + "executionSuccessful": false, + "toolExecutionNotifications": [], + "endTimeUtc": "2025-07-09T22:43:31Z"}], + "artifacts": [{"location": {"uri": "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c"}, + "sourceLanguage": "c", + "roles": ["analysisTarget"]}], + "results": [{"ruleId": "error", + "level": "error", + "message": {"text": "this is a placeholder error, with graphs"}, + "locations": [{"physicalLocation": {"artifactLocation": {"uri": "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 10}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " here ();"}}}, + "logicalLocations": [{"index": 0, + "fullyQualifiedName": "test_graphs"}]}], + "graphs": [{"description": {"text": "foo"}, + "nodes": [{"id": "a"}, + {"id": "b", + "properties": {"/placeholder-prefix/color": "red"}, + "children": [{"id": "c", + "label": {"text": "I am a node label"}}]}], + "edges": [{"id": "my-edge", + "label": {"text": "I am an edge label"}, + "sourceNodeId": "a", + "targetNodeId": "c"}]}, + {"description": {"text": "bar"}, + "nodes": [{"id": "a"}, + {"id": "b", + "properties": {"/placeholder-prefix/color": "red"}, + "children": [{"id": "c", + "label": {"text": "I am a node label"}}]}], + "edges": [{"id": "my-edge", + "label": {"text": "I am an edge label"}, + "sourceNodeId": "a", + "targetNodeId": "c"}]}]}], + "logicalLocations": [{"name": "test_graphs", + "fullyQualifiedName": "test_graphs", + "decoratedName": "test_graphs", + "kind": "function", + "index": 0}], + "graphs": [{"description": {"text": "Optimization Passes"}, + "nodes": [{"id": "all_lowering_passes", + "label": {"text": "all_lowering_passes"}, + "children": [{"id": "*warn_unused_result_0x101ef3d0", + "label": {"text": "*warn_unused_result"}}, + {"id": "*diagnose_omp_blocks_0x101ef430", + "label": {"text": "*diagnose_omp_blocks"}}, + {"id": "*diagnose_tm_blocks_0x101ef490", + "label": {"text": "*diagnose_tm_blocks"}}, + {"id": "13_omp_oacc_kernels_decompose", + "label": {"text": "13_omp_oacc_kernels_decompose"}}, + {"id": "14_omplower", + "label": {"text": "14_omplower"}}, + {"id": "15_lower", + "label": {"text": "15_lower"}}, + {"id": "16_tmlower", + "label": {"text": "16_tmlower"}}, + {"id": "17_ehopt", + "label": {"text": "17_ehopt"}}, + {"id": "18_eh", + "label": {"text": "18_eh"}}, + {"id": "19_coro-lower-builtins", + "label": {"text": "19_coro-lower-builtins"}}, + {"id": "20_cfg", + "label": {"text": "20_cfg"}}, + {"id": "*warn_function_return_0x101ef7f0", + "label": {"text": "*warn_function_return"}}, + {"id": "21_coro-early-expand-ifns", + "label": {"text": "21_coro-early-expand-ifns"}}, + {"id": "22_ompexp", + "label": {"text": "22_ompexp"}}, + {"id": "*build_cgraph_edges_0x101ef910", + "label": {"text": "*build_cgraph_edges"}}]}, + {"id": "all_small_ipa_passes", + "label": {"text": "all_small_ipa_passes"}, + "children": [{"id": "23_afdo_offline", + "label": {"text": "23_afdo_offline"}}, + {"id": "*free_lang_data_0x101ef9d0", + "label": {"text": "*free_lang_data"}}, + {"id": "24_visibility", + "label": {"text": "24_visibility"}}, + {"id": "25_strubm", + "label": {"text": "25_strubm"}}, + {"id": "26_build_ssa_passes", + "label": {"text": "26_build_ssa_passes"}}, + {"id": "27_fixup_cfg", + "label": {"text": "27_fixup_cfg"}}, + {"id": "28_ssa", + "label": {"text": "28_ssa"}}, + {"id": "376_test_graph_emission", + "label": {"text": "376_test_graph_emission"}}, + {"id": "29_walloca", + "label": {"text": "29_walloca"}}, + {"id": "30_warn-printf", + "label": {"text": "30_warn-printf"}}, + {"id": "*nonnullcmp_0x101efce0", + "label": {"text": "*nonnullcmp"}}, + {"id": "31_early_uninit", + "label": {"text": "31_early_uninit"}}, + {"id": "32_waccess", + "label": {"text": "32_waccess"}}, + {"id": "33_ubsan", + "label": {"text": "33_ubsan"}}, + {"id": "34_nothrow", + "label": {"text": "34_nothrow"}}, + {"id": "*rebuild_cgraph_edges_0x101f0020", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "35_opt_local_passes", + "label": {"text": "35_opt_local_passes"}}, + {"id": "36_fixup_cfg", + "label": {"text": "36_fixup_cfg"}}, + {"id": "*rebuild_cgraph_edges_0x101f0140", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "37_local-fnsummary", + "label": {"text": "37_local-fnsummary"}}, + {"id": "38_einline", + "label": {"text": "38_einline"}}, + {"id": "*infinite-recursion_0x101f0260", + "label": {"text": "*infinite-recursion"}}, + {"id": "39_early_optimizations", + "label": {"text": "39_early_optimizations"}}, + {"id": "*remove_cgraph_callee_edges_0x101f0340", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "40_early_objsz", + "label": {"text": "40_early_objsz"}}, + {"id": "41_ccp", + "label": {"text": "41_ccp"}}, + {"id": "42_forwprop", + "label": {"text": "42_forwprop"}}, + {"id": "43_ethread", + "label": {"text": "43_ethread"}}, + {"id": "44_esra", + "label": {"text": "44_esra"}}, + {"id": "45_ealias", + "label": {"text": "45_ealias"}}, + {"id": "46_phiprop", + "label": {"text": "46_phiprop"}}, + {"id": "47_fre", + "label": {"text": "47_fre"}}, + {"id": "48_evrp", + "label": {"text": "48_evrp"}}, + {"id": "49_mergephi", + "label": {"text": "49_mergephi"}}, + {"id": "50_dse", + "label": {"text": "50_dse"}}, + {"id": "51_cddce", + "label": {"text": "51_cddce"}}, + {"id": "52_phiopt", + "label": {"text": "52_phiopt"}}, + {"id": "53_tailr", + "label": {"text": "53_tailr"}}, + {"id": "54_iftoswitch", + "label": {"text": "54_iftoswitch"}}, + {"id": "55_switchconv", + "label": {"text": "55_switchconv"}}, + {"id": "56_ehcleanup", + "label": {"text": "56_ehcleanup"}}, + {"id": "57_sccopy", + "label": {"text": "57_sccopy"}}, + {"id": "58_profile_estimate", + "label": {"text": "58_profile_estimate"}}, + {"id": "59_local-pure-const", + "label": {"text": "59_local-pure-const"}}, + {"id": "60_modref", + "label": {"text": "60_modref"}}, + {"id": "61_fnsplit", + "label": {"text": "61_fnsplit"}}, + {"id": "*strip_predict_hints_0x101f0c60", + "label": {"text": "*strip_predict_hints"}}, + {"id": "62_release_ssa", + "label": {"text": "62_release_ssa"}}, + {"id": "*rebuild_cgraph_edges_0x101f0d30", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "63_local-fnsummary", + "label": {"text": "63_local-fnsummary"}}, + {"id": "64_remove_symbols", + "label": {"text": "64_remove_symbols"}}, + {"id": "65_strub", + "label": {"text": "65_strub"}}, + {"id": "66_ipa_oacc", + "label": {"text": "66_ipa_oacc"}}, + {"id": "67_pta", + "label": {"text": "67_pta"}}, + {"id": "68_ipa_oacc_kernels", + "label": {"text": "68_ipa_oacc_kernels"}}, + {"id": "69_oacc_kernels", + "label": {"text": "69_oacc_kernels"}}, + {"id": "70_ch", + "label": {"text": "70_ch"}}, + {"id": "71_fre", + "label": {"text": "71_fre"}}, + {"id": "72_lim", + "label": {"text": "72_lim"}}, + {"id": "73_dom", + "label": {"text": "73_dom"}}, + {"id": "74_dce", + "label": {"text": "74_dce"}}, + {"id": "75_parloops", + "label": {"text": "75_parloops"}}, + {"id": "76_ompexpssa", + "label": {"text": "76_ompexpssa"}}, + {"id": "*rebuild_cgraph_edges_0x101f1310", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "77_targetclone", + "label": {"text": "77_targetclone"}}, + {"id": "78_afdo", + "label": {"text": "78_afdo"}}, + {"id": "79_feedback_fnsplit", + "label": {"text": "79_feedback_fnsplit"}}, + {"id": "80_profile", + "label": {"text": "80_profile"}}, + {"id": "81_feedback_fnsplit", + "label": {"text": "81_feedback_fnsplit"}}, + {"id": "82_free-fnsummary", + "label": {"text": "82_free-fnsummary"}}, + {"id": "83_increase_alignment", + "label": {"text": "83_increase_alignment"}}, + {"id": "84_tmipa", + "label": {"text": "84_tmipa"}}, + {"id": "85_emutls", + "label": {"text": "85_emutls"}}]}, + {"id": "all_regular_ipa_passes", + "label": {"text": "all_regular_ipa_passes"}, + "children": [{"id": "86_analyzer", + "label": {"text": "86_analyzer"}}, + {"id": "87_odr", + "label": {"text": "87_odr"}}, + {"id": "88_whole-program", + "label": {"text": "88_whole-program"}}, + {"id": "89_profile_estimate", + "label": {"text": "89_profile_estimate"}}, + {"id": "90_icf", + "label": {"text": "90_icf"}}, + {"id": "91_devirt", + "label": {"text": "91_devirt"}}, + {"id": "92_cdtor", + "label": {"text": "92_cdtor"}}, + {"id": "93_cp", + "label": {"text": "93_cp"}}, + {"id": "94_sra", + "label": {"text": "94_sra"}}, + {"id": "95_fnsummary", + "label": {"text": "95_fnsummary"}}, + {"id": "96_inline", + "label": {"text": "96_inline"}}, + {"id": "97_locality-clone", + "label": {"text": "97_locality-clone"}}, + {"id": "98_pure-const", + "label": {"text": "98_pure-const"}}, + {"id": "99_modref", + "label": {"text": "99_modref"}}, + {"id": "100_free-fnsummary", + "label": {"text": "100_free-fnsummary"}}, + {"id": "101_static-var", + "label": {"text": "101_static-var"}}, + {"id": "102_single-use", + "label": {"text": "102_single-use"}}, + {"id": "103_comdats", + "label": {"text": "103_comdats"}}]}, + {"id": "all_late_ipa_passes", + "label": {"text": "all_late_ipa_passes"}, + "children": [{"id": "104_pta", + "label": {"text": "104_pta"}}, + {"id": "105_simdclone", + "label": {"text": "105_simdclone"}}]}, + {"id": "all_passes", + "label": {"text": "all_passes"}, + "children": [{"id": "106_fixup_cfg", + "label": {"text": "106_fixup_cfg"}}, + {"id": "107_ehdisp", + "label": {"text": "107_ehdisp"}}, + {"id": "108_oaccloops", + "label": {"text": "108_oaccloops"}}, + {"id": "109_omp_oacc_neuter_broadcast", + "label": {"text": "109_omp_oacc_neuter_broadcast"}}, + {"id": "110_oaccdevlow", + "label": {"text": "110_oaccdevlow"}}, + {"id": "111_ompdevlow", + "label": {"text": "111_ompdevlow"}}, + {"id": "112_omptargetlink", + "label": {"text": "112_omptargetlink"}}, + {"id": "113_adjust_alignment", + "label": {"text": "113_adjust_alignment"}}, + {"id": "114_hardcfr", + "label": {"text": "114_hardcfr"}}, + {"id": "*all_optimizations_0x101f2720", + "label": {"text": "*all_optimizations"}}, + {"id": "*remove_cgraph_callee_edges_0x101f2780", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "*strip_predict_hints_0x101f27e0", + "label": {"text": "*strip_predict_hints"}}, + {"id": "115_ccp", + "label": {"text": "115_ccp"}}, + {"id": "116_objsz", + "label": {"text": "116_objsz"}}, + {"id": "117_post_ipa_warn", + "label": {"text": "117_post_ipa_warn"}}, + {"id": "118_waccess", + "label": {"text": "118_waccess"}}, + {"id": "119_rebuild_frequencies", + "label": {"text": "119_rebuild_frequencies"}}, + {"id": "120_cunrolli", + "label": {"text": "120_cunrolli"}}, + {"id": "121_backprop", + "label": {"text": "121_backprop"}}, + {"id": "122_phiprop", + "label": {"text": "122_phiprop"}}, + {"id": "123_forwprop", + "label": {"text": "123_forwprop"}}, + {"id": "124_alias", + "label": {"text": "124_alias"}}, + {"id": "125_retslot", + "label": {"text": "125_retslot"}}, + {"id": "126_fre", + "label": {"text": "126_fre"}}, + {"id": "127_mergephi", + "label": {"text": "127_mergephi"}}, + {"id": "128_threadfull", + "label": {"text": "128_threadfull"}}, + {"id": "129_vrp", + "label": {"text": "129_vrp"}}, + {"id": "130_bounds", + "label": {"text": "130_bounds"}}, + {"id": "131_dse", + "label": {"text": "131_dse"}}, + {"id": "132_dce", + "label": {"text": "132_dce"}}, + {"id": "133_stdarg", + "label": {"text": "133_stdarg"}}, + {"id": "134_cdce", + "label": {"text": "134_cdce"}}, + {"id": "135_cselim", + "label": {"text": "135_cselim"}}, + {"id": "136_copyprop", + "label": {"text": "136_copyprop"}}, + {"id": "137_ifcombine", + "label": {"text": "137_ifcombine"}}, + {"id": "138_mergephi", + "label": {"text": "138_mergephi"}}, + {"id": "139_phiopt", + "label": {"text": "139_phiopt"}}, + {"id": "140_tailr", + "label": {"text": "140_tailr"}}, + {"id": "141_ch", + "label": {"text": "141_ch"}}, + {"id": "142_cplxlower", + "label": {"text": "142_cplxlower"}}, + {"id": "143_bitintlower", + "label": {"text": "143_bitintlower"}}, + {"id": "144_sra", + "label": {"text": "144_sra"}}, + {"id": "145_thread", + "label": {"text": "145_thread"}}, + {"id": "146_dom", + "label": {"text": "146_dom"}}, + {"id": "147_copyprop", + "label": {"text": "147_copyprop"}}, + {"id": "148_isolate-paths", + "label": {"text": "148_isolate-paths"}}, + {"id": "149_reassoc", + "label": {"text": "149_reassoc"}}, + {"id": "150_dce", + "label": {"text": "150_dce"}}, + {"id": "151_forwprop", + "label": {"text": "151_forwprop"}}, + {"id": "152_phiopt", + "label": {"text": "152_phiopt"}}, + {"id": "153_ccp", + "label": {"text": "153_ccp"}}, + {"id": "154_pow", + "label": {"text": "154_pow"}}, + {"id": "155_bswap", + "label": {"text": "155_bswap"}}, + {"id": "156_laddress", + "label": {"text": "156_laddress"}}, + {"id": "157_lim", + "label": {"text": "157_lim"}}, + {"id": "158_walloca", + "label": {"text": "158_walloca"}}, + {"id": "159_pre", + "label": {"text": "159_pre"}}, + {"id": "160_sink", + "label": {"text": "160_sink"}}, + {"id": "161_sancov", + "label": {"text": "161_sancov"}}, + {"id": "162_asan", + "label": {"text": "162_asan"}}, + {"id": "163_tsan", + "label": {"text": "163_tsan"}}, + {"id": "164_dse", + "label": {"text": "164_dse"}}, + {"id": "165_dce", + "label": {"text": "165_dce"}}, + {"id": "166_fix_loops", + "label": {"text": "166_fix_loops"}}, + {"id": "167_loop", + "label": {"text": "167_loop"}}, + {"id": "168_loopinit", + "label": {"text": "168_loopinit"}}, + {"id": "169_unswitch", + "label": {"text": "169_unswitch"}}, + {"id": "170_lsplit", + "label": {"text": "170_lsplit"}}, + {"id": "171_sccp", + "label": {"text": "171_sccp"}}, + {"id": "172_lversion", + "label": {"text": "172_lversion"}}, + {"id": "173_unrolljam", + "label": {"text": "173_unrolljam"}}, + {"id": "174_cddce", + "label": {"text": "174_cddce"}}, + {"id": "175_ivcanon", + "label": {"text": "175_ivcanon"}}, + {"id": "176_ldist", + "label": {"text": "176_ldist"}}, + {"id": "177_crc", + "label": {"text": "177_crc"}}, + {"id": "178_linterchange", + "label": {"text": "178_linterchange"}}, + {"id": "179_copyprop", + "label": {"text": "179_copyprop"}}, + {"id": "180_graphite0", + "label": {"text": "180_graphite0"}}, + {"id": "181_graphite", + "label": {"text": "181_graphite"}}, + {"id": "182_lim", + "label": {"text": "182_lim"}}, + {"id": "183_copyprop", + "label": {"text": "183_copyprop"}}, + {"id": "184_dce", + "label": {"text": "184_dce"}}, + {"id": "185_parloops", + "label": {"text": "185_parloops"}}, + {"id": "186_ompexpssa", + "label": {"text": "186_ompexpssa"}}, + {"id": "187_ch_vect", + "label": {"text": "187_ch_vect"}}, + {"id": "188_ifcvt", + "label": {"text": "188_ifcvt"}}, + {"id": "189_vect", + "label": {"text": "189_vect"}}, + {"id": "190_dce", + "label": {"text": "190_dce"}}, + {"id": "191_pcom", + "label": {"text": "191_pcom"}}, + {"id": "192_cunroll", + "label": {"text": "192_cunroll"}}, + {"id": "*pre_slp_scalar_cleanup_0x101f4880", + "label": {"text": "*pre_slp_scalar_cleanup"}}, + {"id": "193_fre", + "label": {"text": "193_fre"}}, + {"id": "194_dse", + "label": {"text": "194_dse"}}, + {"id": "195_slp", + "label": {"text": "195_slp"}}, + {"id": "196_aprefetch", + "label": {"text": "196_aprefetch"}}, + {"id": "197_ivopts", + "label": {"text": "197_ivopts"}}, + {"id": "198_lim", + "label": {"text": "198_lim"}}, + {"id": "199_loopdone", + "label": {"text": "199_loopdone"}}, + {"id": "200_no_loop", + "label": {"text": "200_no_loop"}}, + {"id": "201_slp", + "label": {"text": "201_slp"}}, + {"id": "202_simduid", + "label": {"text": "202_simduid"}}, + {"id": "203_veclower2", + "label": {"text": "203_veclower2"}}, + {"id": "204_switchlower", + "label": {"text": "204_switchlower"}}, + {"id": "205_sincos", + "label": {"text": "205_sincos"}}, + {"id": "206_recip", + "label": {"text": "206_recip"}}, + {"id": "207_reassoc", + "label": {"text": "207_reassoc"}}, + {"id": "208_slsr", + "label": {"text": "208_slsr"}}, + {"id": "209_split-paths", + "label": {"text": "209_split-paths"}}, + {"id": "210_tracer", + "label": {"text": "210_tracer"}}, + {"id": "211_fre", + "label": {"text": "211_fre"}}, + {"id": "212_thread", + "label": {"text": "212_thread"}}, + {"id": "213_dom", + "label": {"text": "213_dom"}}, + {"id": "214_strlen", + "label": {"text": "214_strlen"}}, + {"id": "215_threadfull", + "label": {"text": "215_threadfull"}}, + {"id": "216_vrp", + "label": {"text": "216_vrp"}}, + {"id": "217_ccp", + "label": {"text": "217_ccp"}}, + {"id": "218_wrestrict", + "label": {"text": "218_wrestrict"}}, + {"id": "219_dse", + "label": {"text": "219_dse"}}, + {"id": "220_dce", + "label": {"text": "220_dce"}}, + {"id": "221_forwprop", + "label": {"text": "221_forwprop"}}, + {"id": "222_sink", + "label": {"text": "222_sink"}}, + {"id": "223_phiopt", + "label": {"text": "223_phiopt"}}, + {"id": "224_fab", + "label": {"text": "224_fab"}}, + {"id": "225_widening_mul", + "label": {"text": "225_widening_mul"}}, + {"id": "226_store-merging", + "label": {"text": "226_store-merging"}}, + {"id": "227_cddce", + "label": {"text": "227_cddce"}}, + {"id": "228_sccopy", + "label": {"text": "228_sccopy"}}, + {"id": "229_tailc", + "label": {"text": "229_tailc"}}, + {"id": "230_crited", + "label": {"text": "230_crited"}}, + {"id": "231_uninit", + "label": {"text": "231_uninit"}}, + {"id": "232_local-pure-const", + "label": {"text": "232_local-pure-const"}}, + {"id": "233_modref", + "label": {"text": "233_modref"}}, + {"id": "234_uncprop", + "label": {"text": "234_uncprop"}}, + {"id": "*all_optimizations_g_0x101f5af0", + "label": {"text": "*all_optimizations_g"}}, + {"id": "*remove_cgraph_callee_edges_0x101f5b50", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "*strip_predict_hints_0x101f5bb0", + "label": {"text": "*strip_predict_hints"}}, + {"id": "235_cplxlower", + "label": {"text": "235_cplxlower"}}, + {"id": "236_bitintlower", + "label": {"text": "236_bitintlower"}}, + {"id": "237_veclower2", + "label": {"text": "237_veclower2"}}, + {"id": "238_switchlower", + "label": {"text": "238_switchlower"}}, + {"id": "239_ccp", + "label": {"text": "239_ccp"}}, + {"id": "240_post_ipa_warn", + "label": {"text": "240_post_ipa_warn"}}, + {"id": "241_objsz", + "label": {"text": "241_objsz"}}, + {"id": "242_fab", + "label": {"text": "242_fab"}}, + {"id": "243_strlen", + "label": {"text": "243_strlen"}}, + {"id": "244_copyprop", + "label": {"text": "244_copyprop"}}, + {"id": "245_dce", + "label": {"text": "245_dce"}}, + {"id": "246_rebuild_frequencies", + "label": {"text": "246_rebuild_frequencies"}}, + {"id": "247_sancov", + "label": {"text": "247_sancov"}}, + {"id": "248_asan", + "label": {"text": "248_asan"}}, + {"id": "249_tsan", + "label": {"text": "249_tsan"}}, + {"id": "250_crited", + "label": {"text": "250_crited"}}, + {"id": "251_uninit", + "label": {"text": "251_uninit"}}, + {"id": "252_uncprop", + "label": {"text": "252_uncprop"}}, + {"id": "253_assumptions", + "label": {"text": "253_assumptions"}}, + {"id": "*tminit_0x101f6370", + "label": {"text": "*tminit"}}, + {"id": "254_tmmark", + "label": {"text": "254_tmmark"}}, + {"id": "255_tmmemopt", + "label": {"text": "255_tmmemopt"}}, + {"id": "256_tmedge", + "label": {"text": "256_tmedge"}}, + {"id": "257_simduid", + "label": {"text": "257_simduid"}}, + {"id": "258_vtable-verify", + "label": {"text": "258_vtable-verify"}}, + {"id": "259_lower_vaarg", + "label": {"text": "259_lower_vaarg"}}, + {"id": "260_veclower", + "label": {"text": "260_veclower"}}, + {"id": "261_cplxlower0", + "label": {"text": "261_cplxlower0"}}, + {"id": "262_bitintlower0", + "label": {"text": "262_bitintlower0"}}, + {"id": "263_sancov_O0", + "label": {"text": "263_sancov_O0"}}, + {"id": "264_switchlower_O0", + "label": {"text": "264_switchlower_O0"}}, + {"id": "265_asan0", + "label": {"text": "265_asan0"}}, + {"id": "266_tsan0", + "label": {"text": "266_tsan0"}}, + {"id": "267_musttail", + "label": {"text": "267_musttail"}}, + {"id": "268_sanopt", + "label": {"text": "268_sanopt"}}, + {"id": "269_ehcleanup", + "label": {"text": "269_ehcleanup"}}, + {"id": "270_resx", + "label": {"text": "270_resx"}}, + {"id": "271_nrv", + "label": {"text": "271_nrv"}}, + {"id": "272_isel", + "label": {"text": "272_isel"}}, + {"id": "273_hardcbr", + "label": {"text": "273_hardcbr"}}, + {"id": "274_hardcmp", + "label": {"text": "274_hardcmp"}}, + {"id": "275_waccess", + "label": {"text": "275_waccess"}}, + {"id": "276_optimized", + "label": {"text": "276_optimized"}}, + {"id": "*warn_function_noreturn_0x101f6dd0", + "label": {"text": "*warn_function_noreturn"}}, + {"id": "277_expand", + "label": {"text": "277_expand"}}, + {"id": "*rest_of_compilation_0x101f6e90", + "label": {"text": "*rest_of_compilation"}}, + {"id": "278_vregs", + "label": {"text": "278_vregs"}}, + {"id": "279_into_cfglayout", + "label": {"text": "279_into_cfglayout"}}, + {"id": "280_jump", + "label": {"text": "280_jump"}}, + {"id": "281_subreg1", + "label": {"text": "281_subreg1"}}, + {"id": "282_dfinit", + "label": {"text": "282_dfinit"}}, + {"id": "283_cse1", + "label": {"text": "283_cse1"}}, + {"id": "284_fwprop1", + "label": {"text": "284_fwprop1"}}, + {"id": "285_cprop", + "label": {"text": "285_cprop"}}, + {"id": "286_rtl pre", + "label": {"text": "286_rtl pre"}}, + {"id": "287_hoist", + "label": {"text": "287_hoist"}}, + {"id": "288_hardreg_pre", + "label": {"text": "288_hardreg_pre"}}, + {"id": "289_cprop", + "label": {"text": "289_cprop"}}, + {"id": "290_store_motion", + "label": {"text": "290_store_motion"}}, + {"id": "291_cse_local", + "label": {"text": "291_cse_local"}}, + {"id": "292_ce1", + "label": {"text": "292_ce1"}}, + {"id": "293_apx_nfcvt", + "label": {"text": "293_apx_nfcvt"}}, + {"id": "294_reginfo", + "label": {"text": "294_reginfo"}}, + {"id": "295_loop2", + "label": {"text": "295_loop2"}}, + {"id": "296_loop2_init", + "label": {"text": "296_loop2_init"}}, + {"id": "297_loop2_invariant", + "label": {"text": "297_loop2_invariant"}}, + {"id": "298_loop2_unroll", + "label": {"text": "298_loop2_unroll"}}, + {"id": "299_loop2_doloop", + "label": {"text": "299_loop2_doloop"}}, + {"id": "300_loop2_done", + "label": {"text": "300_loop2_done"}}, + {"id": "301_subreg2", + "label": {"text": "301_subreg2"}}, + {"id": "302_web", + "label": {"text": "302_web"}}, + {"id": "303_cprop", + "label": {"text": "303_cprop"}}, + {"id": "304_stv", + "label": {"text": "304_stv"}}, + {"id": "305_cse2", + "label": {"text": "305_cse2"}}, + {"id": "306_dse1", + "label": {"text": "306_dse1"}}, + {"id": "307_fwprop2", + "label": {"text": "307_fwprop2"}}, + {"id": "308_auto_inc_dec", + "label": {"text": "308_auto_inc_dec"}}, + {"id": "309_init-regs", + "label": {"text": "309_init-regs"}}, + {"id": "310_ud_dce", + "label": {"text": "310_ud_dce"}}, + {"id": "311_ext_dce", + "label": {"text": "311_ext_dce"}}, + {"id": "312_combine", + "label": {"text": "312_combine"}}, + {"id": "313_late_combine", + "label": {"text": "313_late_combine"}}, + {"id": "314_rpad", + "label": {"text": "314_rpad"}}, + {"id": "315_rrvl", + "label": {"text": "315_rrvl"}}, + {"id": "316_stv", + "label": {"text": "316_stv"}}, + {"id": "317_ce2", + "label": {"text": "317_ce2"}}, + {"id": "318_jump_after_combine", + "label": {"text": "318_jump_after_combine"}}, + {"id": "319_bbpart", + "label": {"text": "319_bbpart"}}, + {"id": "320_outof_cfglayout", + "label": {"text": "320_outof_cfglayout"}}, + {"id": "321_split1", + "label": {"text": "321_split1"}}, + {"id": "322_subreg3", + "label": {"text": "322_subreg3"}}, + {"id": "323_no-opt dfinit", + "label": {"text": "323_no-opt dfinit"}}, + {"id": "*stack_ptr_mod_0x101f8050", + "label": {"text": "*stack_ptr_mod"}}, + {"id": "324_mode_sw", + "label": {"text": "324_mode_sw"}}, + {"id": "325_asmcons", + "label": {"text": "325_asmcons"}}, + {"id": "326_sms", + "label": {"text": "326_sms"}}, + {"id": "327_lr_shrinkage", + "label": {"text": "327_lr_shrinkage"}}, + {"id": "328_sched1", + "label": {"text": "328_sched1"}}, + {"id": "329_avoid_store_forwarding", + "label": {"text": "329_avoid_store_forwarding"}}, + {"id": "330_early_remat", + "label": {"text": "330_early_remat"}}, + {"id": "331_ira", + "label": {"text": "331_ira"}}, + {"id": "332_reload", + "label": {"text": "332_reload"}}, + {"id": "*all-postreload_0x101f8410", + "label": {"text": "*all-postreload"}}, + {"id": "333_postreload", + "label": {"text": "333_postreload"}}, + {"id": "334_vzeroupper", + "label": {"text": "334_vzeroupper"}}, + {"id": "335_late_combine", + "label": {"text": "335_late_combine"}}, + {"id": "336_gcse2", + "label": {"text": "336_gcse2"}}, + {"id": "337_split2", + "label": {"text": "337_split2"}}, + {"id": "338_ree", + "label": {"text": "338_ree"}}, + {"id": "339_cmpelim", + "label": {"text": "339_cmpelim"}}, + {"id": "340_pro_and_epilogue", + "label": {"text": "340_pro_and_epilogue"}}, + {"id": "341_dse2", + "label": {"text": "341_dse2"}}, + {"id": "342_csa", + "label": {"text": "342_csa"}}, + {"id": "343_jump2", + "label": {"text": "343_jump2"}}, + {"id": "344_compgotos", + "label": {"text": "344_compgotos"}}, + {"id": "345_sched_fusion", + "label": {"text": "345_sched_fusion"}}, + {"id": "346_peephole2", + "label": {"text": "346_peephole2"}}, + {"id": "347_ce3", + "label": {"text": "347_ce3"}}, + {"id": "348_rnreg", + "label": {"text": "348_rnreg"}}, + {"id": "349_fold_mem_offsets", + "label": {"text": "349_fold_mem_offsets"}}, + {"id": "350_cprop_hardreg", + "label": {"text": "350_cprop_hardreg"}}, + {"id": "351_rtl_dce", + "label": {"text": "351_rtl_dce"}}, + {"id": "352_bbro", + "label": {"text": "352_bbro"}}, + {"id": "*leaf_regs_0x101f8bf0", + "label": {"text": "*leaf_regs"}}, + {"id": "353_split3", + "label": {"text": "353_split3"}}, + {"id": "354_sched2", + "label": {"text": "354_sched2"}}, + {"id": "*stack_regs_0x101f8d10", + "label": {"text": "*stack_regs"}}, + {"id": "355_split4", + "label": {"text": "355_split4"}}, + {"id": "356_stack", + "label": {"text": "356_stack"}}, + {"id": "357_late_pro_and_epilogue", + "label": {"text": "357_late_pro_and_epilogue"}}, + {"id": "*all-late_compilation_0x101f8e90", + "label": {"text": "*all-late_compilation"}}, + {"id": "358_zero_call_used_regs", + "label": {"text": "358_zero_call_used_regs"}}, + {"id": "359_alignments", + "label": {"text": "359_alignments"}}, + {"id": "360_vartrack", + "label": {"text": "360_vartrack"}}, + {"id": "*free_cfg_0x101f9010", + "label": {"text": "*free_cfg"}}, + {"id": "361_mach", + "label": {"text": "361_mach"}}, + {"id": "362_barriers", + "label": {"text": "362_barriers"}}, + {"id": "363_dbr", + "label": {"text": "363_dbr"}}, + {"id": "364_split5", + "label": {"text": "364_split5"}}, + {"id": "365_eh_ranges", + "label": {"text": "365_eh_ranges"}}, + {"id": "366_endbr_and_patchable_area", + "label": {"text": "366_endbr_and_patchable_area"}}, + {"id": "367_align_tight_loops", + "label": {"text": "367_align_tight_loops"}}, + {"id": "368_shorten", + "label": {"text": "368_shorten"}}, + {"id": "369_nothrow", + "label": {"text": "369_nothrow"}}, + {"id": "370_dwarf2", + "label": {"text": "370_dwarf2"}}, + {"id": "371_final", + "label": {"text": "371_final"}}, + {"id": "372_dfinish", + "label": {"text": "372_dfinish"}}, + {"id": "*clean_state_0x101f9500", + "label": {"text": "*clean_state"}}]}], + "edges": [{"id": "edge0", + "label": {"text": "next"}, + "sourceNodeId": "22_ompexp", + "targetNodeId": "*build_cgraph_edges_0x101ef910"}, + {"id": "edge1", + "label": {"text": "next"}, + "sourceNodeId": "21_coro-early-expand-ifns", + "targetNodeId": "22_ompexp"}, + {"id": "edge2", + "label": {"text": "next"}, + "sourceNodeId": "*warn_function_return_0x101ef7f0", + "targetNodeId": "21_coro-early-expand-ifns"}, + {"id": "edge3", + "label": {"text": "next"}, + "sourceNodeId": "20_cfg", + "targetNodeId": "*warn_function_return_0x101ef7f0"}, + {"id": "edge4", + "label": {"text": "next"}, + "sourceNodeId": "19_coro-lower-builtins", + "targetNodeId": "20_cfg"}, + {"id": "edge5", + "label": {"text": "next"}, + "sourceNodeId": "18_eh", + "targetNodeId": "19_coro-lower-builtins"}, + {"id": "edge6", + "label": {"text": "next"}, + "sourceNodeId": "17_ehopt", + "targetNodeId": "18_eh"}, + {"id": "edge7", + "label": {"text": "next"}, + "sourceNodeId": "16_tmlower", + "targetNodeId": "17_ehopt"}, + {"id": "edge8", + "label": {"text": "next"}, + "sourceNodeId": "15_lower", + "targetNodeId": "16_tmlower"}, + {"id": "edge9", + "label": {"text": "next"}, + "sourceNodeId": "14_omplower", + "targetNodeId": "15_lower"}, + {"id": "edge10", + "label": {"text": "next"}, + "sourceNodeId": "13_omp_oacc_kernels_decompose", + "targetNodeId": "14_omplower"}, + {"id": "edge11", + "label": {"text": "next"}, + "sourceNodeId": "*diagnose_tm_blocks_0x101ef490", + "targetNodeId": "13_omp_oacc_kernels_decompose"}, + {"id": "edge12", + "label": {"text": "next"}, + "sourceNodeId": "*diagnose_omp_blocks_0x101ef430", + "targetNodeId": "*diagnose_tm_blocks_0x101ef490"}, + {"id": "edge13", + "label": {"text": "next"}, + "sourceNodeId": "*warn_unused_result_0x101ef3d0", + "targetNodeId": "*diagnose_omp_blocks_0x101ef430"}, + {"id": "edge14", + "label": {"text": "next"}, + "sourceNodeId": "34_nothrow", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0020"}, + {"id": "edge15", + "label": {"text": "next"}, + "sourceNodeId": "33_ubsan", + "targetNodeId": "34_nothrow"}, + {"id": "edge16", + "label": {"text": "next"}, + "sourceNodeId": "32_waccess", + "targetNodeId": "33_ubsan"}, + {"id": "edge17", + "label": {"text": "next"}, + "sourceNodeId": "31_early_uninit", + "targetNodeId": "32_waccess"}, + {"id": "edge18", + "label": {"text": "next"}, + "sourceNodeId": "*nonnullcmp_0x101efce0", + "targetNodeId": "31_early_uninit"}, + {"id": "edge19", + "label": {"text": "next"}, + "sourceNodeId": "30_warn-printf", + "targetNodeId": "*nonnullcmp_0x101efce0"}, + {"id": "edge20", + "label": {"text": "next"}, + "sourceNodeId": "29_walloca", + "targetNodeId": "30_warn-printf"}, + {"id": "edge21", + "label": {"text": "next"}, + "sourceNodeId": "376_test_graph_emission", + "targetNodeId": "29_walloca"}, + {"id": "edge22", + "label": {"text": "next"}, + "sourceNodeId": "28_ssa", + "targetNodeId": "376_test_graph_emission"}, + {"id": "edge23", + "label": {"text": "next"}, + "sourceNodeId": "27_fixup_cfg", + "targetNodeId": "28_ssa"}, + {"id": "edge24", + "label": {"text": "sub"}, + "sourceNodeId": "26_build_ssa_passes", + "targetNodeId": "27_fixup_cfg"}, + {"id": "edge25", + "label": {"text": "next"}, + "sourceNodeId": "61_fnsplit", + "targetNodeId": "*strip_predict_hints_0x101f0c60"}, + {"id": "edge26", + "label": {"text": "next"}, + "sourceNodeId": "60_modref", + "targetNodeId": "61_fnsplit"}, + {"id": "edge27", + "label": {"text": "next"}, + "sourceNodeId": "59_local-pure-const", + "targetNodeId": "60_modref"}, + {"id": "edge28", + "label": {"text": "next"}, + "sourceNodeId": "58_profile_estimate", + "targetNodeId": "59_local-pure-const"}, + {"id": "edge29", + "label": {"text": "next"}, + "sourceNodeId": "57_sccopy", + "targetNodeId": "58_profile_estimate"}, + {"id": "edge30", + "label": {"text": "next"}, + "sourceNodeId": "56_ehcleanup", + "targetNodeId": "57_sccopy"}, + {"id": "edge31", + "label": {"text": "next"}, + "sourceNodeId": "55_switchconv", + "targetNodeId": "56_ehcleanup"}, + {"id": "edge32", + "label": {"text": "next"}, + "sourceNodeId": "54_iftoswitch", + "targetNodeId": "55_switchconv"}, + {"id": "edge33", + "label": {"text": "next"}, + "sourceNodeId": "53_tailr", + "targetNodeId": "54_iftoswitch"}, + {"id": "edge34", + "label": {"text": "next"}, + "sourceNodeId": "52_phiopt", + "targetNodeId": "53_tailr"}, + {"id": "edge35", + "label": {"text": "next"}, + "sourceNodeId": "51_cddce", + "targetNodeId": "52_phiopt"}, + {"id": "edge36", + "label": {"text": "next"}, + "sourceNodeId": "50_dse", + "targetNodeId": "51_cddce"}, + {"id": "edge37", + "label": {"text": "next"}, + "sourceNodeId": "49_mergephi", + "targetNodeId": "50_dse"}, + {"id": "edge38", + "label": {"text": "next"}, + "sourceNodeId": "48_evrp", + "targetNodeId": "49_mergephi"}, + {"id": "edge39", + "label": {"text": "next"}, + "sourceNodeId": "47_fre", + "targetNodeId": "48_evrp"}, + {"id": "edge40", + "label": {"text": "next"}, + "sourceNodeId": "46_phiprop", + "targetNodeId": "47_fre"}, + {"id": "edge41", + "label": {"text": "next"}, + "sourceNodeId": "45_ealias", + "targetNodeId": "46_phiprop"}, + {"id": "edge42", + "label": {"text": "next"}, + "sourceNodeId": "44_esra", + "targetNodeId": "45_ealias"}, + {"id": "edge43", + "label": {"text": "next"}, + "sourceNodeId": "43_ethread", + "targetNodeId": "44_esra"}, + {"id": "edge44", + "label": {"text": "next"}, + "sourceNodeId": "42_forwprop", + "targetNodeId": "43_ethread"}, + {"id": "edge45", + "label": {"text": "next"}, + "sourceNodeId": "41_ccp", + "targetNodeId": "42_forwprop"}, + {"id": "edge46", + "label": {"text": "next"}, + "sourceNodeId": "40_early_objsz", + "targetNodeId": "41_ccp"}, + {"id": "edge47", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f0340", + "targetNodeId": "40_early_objsz"}, + {"id": "edge48", + "label": {"text": "sub"}, + "sourceNodeId": "39_early_optimizations", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f0340"}, + {"id": "edge49", + "label": {"text": "next"}, + "sourceNodeId": "*rebuild_cgraph_edges_0x101f0d30", + "targetNodeId": "63_local-fnsummary"}, + {"id": "edge50", + "label": {"text": "next"}, + "sourceNodeId": "62_release_ssa", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0d30"}, + {"id": "edge51", + "label": {"text": "next"}, + "sourceNodeId": "39_early_optimizations", + "targetNodeId": "62_release_ssa"}, + {"id": "edge52", + "label": {"text": "next"}, + "sourceNodeId": "*infinite-recursion_0x101f0260", + "targetNodeId": "39_early_optimizations"}, + {"id": "edge53", + "label": {"text": "next"}, + "sourceNodeId": "38_einline", + "targetNodeId": "*infinite-recursion_0x101f0260"}, + {"id": "edge54", + "label": {"text": "next"}, + "sourceNodeId": "37_local-fnsummary", + "targetNodeId": "38_einline"}, + {"id": "edge55", + "label": {"text": "next"}, + "sourceNodeId": "*rebuild_cgraph_edges_0x101f0140", + "targetNodeId": "37_local-fnsummary"}, + {"id": "edge56", + "label": {"text": "next"}, + "sourceNodeId": "36_fixup_cfg", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0140"}, + {"id": "edge57", + "label": {"text": "sub"}, + "sourceNodeId": "35_opt_local_passes", + "targetNodeId": "36_fixup_cfg"}, + {"id": "edge58", + "label": {"text": "next"}, + "sourceNodeId": "76_ompexpssa", + "targetNodeId": "*rebuild_cgraph_edges_0x101f1310"}, + {"id": "edge59", + "label": {"text": "next"}, + "sourceNodeId": "75_parloops", + "targetNodeId": "76_ompexpssa"}, + {"id": "edge60", + "label": {"text": "next"}, + "sourceNodeId": "74_dce", + "targetNodeId": "75_parloops"}, + {"id": "edge61", + "label": {"text": "next"}, + "sourceNodeId": "73_dom", + "targetNodeId": "74_dce"}, + {"id": "edge62", + "label": {"text": "next"}, + "sourceNodeId": "72_lim", + "targetNodeId": "73_dom"}, + {"id": "edge63", + "label": {"text": "next"}, + "sourceNodeId": "71_fre", + "targetNodeId": "72_lim"}, + {"id": "edge64", + "label": {"text": "next"}, + "sourceNodeId": "70_ch", + "targetNodeId": "71_fre"}, + {"id": "edge65", + "label": {"text": "sub"}, + "sourceNodeId": "69_oacc_kernels", + "targetNodeId": "70_ch"}, + {"id": "edge66", + "label": {"text": "sub"}, + "sourceNodeId": "68_ipa_oacc_kernels", + "targetNodeId": "69_oacc_kernels"}, + {"id": "edge67", + "label": {"text": "next"}, + "sourceNodeId": "67_pta", + "targetNodeId": "68_ipa_oacc_kernels"}, + {"id": "edge68", + "label": {"text": "sub"}, + "sourceNodeId": "66_ipa_oacc", + "targetNodeId": "67_pta"}, + {"id": "edge69", + "label": {"text": "sub"}, + "sourceNodeId": "78_afdo", + "targetNodeId": "79_feedback_fnsplit"}, + {"id": "edge70", + "label": {"text": "sub"}, + "sourceNodeId": "80_profile", + "targetNodeId": "81_feedback_fnsplit"}, + {"id": "edge71", + "label": {"text": "next"}, + "sourceNodeId": "84_tmipa", + "targetNodeId": "85_emutls"}, + {"id": "edge72", + "label": {"text": "next"}, + "sourceNodeId": "83_increase_alignment", + "targetNodeId": "84_tmipa"}, + {"id": "edge73", + "label": {"text": "next"}, + "sourceNodeId": "82_free-fnsummary", + "targetNodeId": "83_increase_alignment"}, + {"id": "edge74", + "label": {"text": "next"}, + "sourceNodeId": "80_profile", + "targetNodeId": "82_free-fnsummary"}, + {"id": "edge75", + "label": {"text": "next"}, + "sourceNodeId": "78_afdo", + "targetNodeId": "80_profile"}, + {"id": "edge76", + "label": {"text": "next"}, + "sourceNodeId": "77_targetclone", + "targetNodeId": "78_afdo"}, + {"id": "edge77", + "label": {"text": "next"}, + "sourceNodeId": "66_ipa_oacc", + "targetNodeId": "77_targetclone"}, + {"id": "edge78", + "label": {"text": "next"}, + "sourceNodeId": "65_strub", + "targetNodeId": "66_ipa_oacc"}, + {"id": "edge79", + "label": {"text": "next"}, + "sourceNodeId": "64_remove_symbols", + "targetNodeId": "65_strub"}, + {"id": "edge80", + "label": {"text": "next"}, + "sourceNodeId": "35_opt_local_passes", + "targetNodeId": "64_remove_symbols"}, + {"id": "edge81", + "label": {"text": "next"}, + "sourceNodeId": "26_build_ssa_passes", + "targetNodeId": "35_opt_local_passes"}, + {"id": "edge82", + "label": {"text": "next"}, + "sourceNodeId": "25_strubm", + "targetNodeId": "26_build_ssa_passes"}, + {"id": "edge83", + "label": {"text": "next"}, + "sourceNodeId": "24_visibility", + "targetNodeId": "25_strubm"}, + {"id": "edge84", + "label": {"text": "next"}, + "sourceNodeId": "*free_lang_data_0x101ef9d0", + "targetNodeId": "24_visibility"}, + {"id": "edge85", + "label": {"text": "next"}, + "sourceNodeId": "23_afdo_offline", + "targetNodeId": "*free_lang_data_0x101ef9d0"}, + {"id": "edge86", + "label": {"text": "next"}, + "sourceNodeId": "102_single-use", + "targetNodeId": "103_comdats"}, + {"id": "edge87", + "label": {"text": "next"}, + "sourceNodeId": "101_static-var", + "targetNodeId": "102_single-use"}, + {"id": "edge88", + "label": {"text": "next"}, + "sourceNodeId": "100_free-fnsummary", + "targetNodeId": "101_static-var"}, + {"id": "edge89", + "label": {"text": "next"}, + "sourceNodeId": "99_modref", + "targetNodeId": "100_free-fnsummary"}, + {"id": "edge90", + "label": {"text": "next"}, + "sourceNodeId": "98_pure-const", + "targetNodeId": "99_modref"}, + {"id": "edge91", + "label": {"text": "next"}, + "sourceNodeId": "97_locality-clone", + "targetNodeId": "98_pure-const"}, + {"id": "edge92", + "label": {"text": "next"}, + "sourceNodeId": "96_inline", + "targetNodeId": "97_locality-clone"}, + {"id": "edge93", + "label": {"text": "next"}, + "sourceNodeId": "95_fnsummary", + "targetNodeId": "96_inline"}, + {"id": "edge94", + "label": {"text": "next"}, + "sourceNodeId": "94_sra", + "targetNodeId": "95_fnsummary"}, + {"id": "edge95", + "label": {"text": "next"}, + "sourceNodeId": "93_cp", + "targetNodeId": "94_sra"}, + {"id": "edge96", + "label": {"text": "next"}, + "sourceNodeId": "92_cdtor", + "targetNodeId": "93_cp"}, + {"id": "edge97", + "label": {"text": "next"}, + "sourceNodeId": "91_devirt", + "targetNodeId": "92_cdtor"}, + {"id": "edge98", + "label": {"text": "next"}, + "sourceNodeId": "90_icf", + "targetNodeId": "91_devirt"}, + {"id": "edge99", + "label": {"text": "next"}, + "sourceNodeId": "89_profile_estimate", + "targetNodeId": "90_icf"}, + {"id": "edge100", + "label": {"text": "next"}, + "sourceNodeId": "88_whole-program", + "targetNodeId": "89_profile_estimate"}, + {"id": "edge101", + "label": {"text": "next"}, + "sourceNodeId": "87_odr", + "targetNodeId": "88_whole-program"}, + {"id": "edge102", + "label": {"text": "next"}, + "sourceNodeId": "86_analyzer", + "targetNodeId": "87_odr"}, + {"id": "edge103", + "label": {"text": "next"}, + "sourceNodeId": "104_pta", + "targetNodeId": "105_simdclone"}, + {"id": "edge104", + "label": {"text": "next"}, + "sourceNodeId": "183_copyprop", + "targetNodeId": "184_dce"}, + {"id": "edge105", + "label": {"text": "next"}, + "sourceNodeId": "182_lim", + "targetNodeId": "183_copyprop"}, + {"id": "edge106", + "label": {"text": "next"}, + "sourceNodeId": "181_graphite", + "targetNodeId": "182_lim"}, + {"id": "edge107", + "label": {"text": "sub"}, + "sourceNodeId": "180_graphite0", + "targetNodeId": "181_graphite"}, + {"id": "edge108", + "label": {"text": "sub"}, + "sourceNodeId": "189_vect", + "targetNodeId": "190_dce"}, + {"id": "edge109", + "label": {"text": "next"}, + "sourceNodeId": "193_fre", + "targetNodeId": "194_dse"}, + {"id": "edge110", + "label": {"text": "sub"}, + "sourceNodeId": "*pre_slp_scalar_cleanup_0x101f4880", + "targetNodeId": "193_fre"}, + {"id": "edge111", + "label": {"text": "next"}, + "sourceNodeId": "198_lim", + "targetNodeId": "199_loopdone"}, + {"id": "edge112", + "label": {"text": "next"}, + "sourceNodeId": "197_ivopts", + "targetNodeId": "198_lim"}, + {"id": "edge113", + "label": {"text": "next"}, + "sourceNodeId": "196_aprefetch", + "targetNodeId": "197_ivopts"}, + {"id": "edge114", + "label": {"text": "next"}, + "sourceNodeId": "195_slp", + "targetNodeId": "196_aprefetch"}, + {"id": "edge115", + "label": {"text": "next"}, + "sourceNodeId": "*pre_slp_scalar_cleanup_0x101f4880", + "targetNodeId": "195_slp"}, + {"id": "edge116", + "label": {"text": "next"}, + "sourceNodeId": "192_cunroll", + "targetNodeId": "*pre_slp_scalar_cleanup_0x101f4880"}, + {"id": "edge117", + "label": {"text": "next"}, + "sourceNodeId": "191_pcom", + "targetNodeId": "192_cunroll"}, + {"id": "edge118", + "label": {"text": "next"}, + "sourceNodeId": "189_vect", + "targetNodeId": "191_pcom"}, + {"id": "edge119", + "label": {"text": "next"}, + "sourceNodeId": "188_ifcvt", + "targetNodeId": "189_vect"}, + {"id": "edge120", + "label": {"text": "next"}, + "sourceNodeId": "187_ch_vect", + "targetNodeId": "188_ifcvt"}, + {"id": "edge121", + "label": {"text": "next"}, + "sourceNodeId": "186_ompexpssa", + "targetNodeId": "187_ch_vect"}, + {"id": "edge122", + "label": {"text": "next"}, + "sourceNodeId": "185_parloops", + "targetNodeId": "186_ompexpssa"}, + {"id": "edge123", + "label": {"text": "next"}, + "sourceNodeId": "180_graphite0", + "targetNodeId": "185_parloops"}, + {"id": "edge124", + "label": {"text": "next"}, + "sourceNodeId": "179_copyprop", + "targetNodeId": "180_graphite0"}, + {"id": "edge125", + "label": {"text": "next"}, + "sourceNodeId": "178_linterchange", + "targetNodeId": "179_copyprop"}, + {"id": "edge126", + "label": {"text": "next"}, + "sourceNodeId": "177_crc", + "targetNodeId": "178_linterchange"}, + {"id": "edge127", + "label": {"text": "next"}, + "sourceNodeId": "176_ldist", + "targetNodeId": "177_crc"}, + {"id": "edge128", + "label": {"text": "next"}, + "sourceNodeId": "175_ivcanon", + "targetNodeId": "176_ldist"}, + {"id": "edge129", + "label": {"text": "next"}, + "sourceNodeId": "174_cddce", + "targetNodeId": "175_ivcanon"}, + {"id": "edge130", + "label": {"text": "next"}, + "sourceNodeId": "173_unrolljam", + "targetNodeId": "174_cddce"}, + {"id": "edge131", + "label": {"text": "next"}, + "sourceNodeId": "172_lversion", + "targetNodeId": "173_unrolljam"}, + {"id": "edge132", + "label": {"text": "next"}, + "sourceNodeId": "171_sccp", + "targetNodeId": "172_lversion"}, + {"id": "edge133", + "label": {"text": "next"}, + "sourceNodeId": "170_lsplit", + "targetNodeId": "171_sccp"}, + {"id": "edge134", + "label": {"text": "next"}, + "sourceNodeId": "169_unswitch", + "targetNodeId": "170_lsplit"}, + {"id": "edge135", + "label": {"text": "next"}, + "sourceNodeId": "168_loopinit", + "targetNodeId": "169_unswitch"}, + {"id": "edge136", + "label": {"text": "sub"}, + "sourceNodeId": "167_loop", + "targetNodeId": "168_loopinit"}, + {"id": "edge137", + "label": {"text": "sub"}, + "sourceNodeId": "200_no_loop", + "targetNodeId": "201_slp"}, + {"id": "edge138", + "label": {"text": "next"}, + "sourceNodeId": "233_modref", + "targetNodeId": "234_uncprop"}, + {"id": "edge139", + "label": {"text": "next"}, + "sourceNodeId": "232_local-pure-const", + "targetNodeId": "233_modref"}, + {"id": "edge140", + "label": {"text": "next"}, + "sourceNodeId": "231_uninit", + "targetNodeId": "232_local-pure-const"}, + {"id": "edge141", + "label": {"text": "next"}, + "sourceNodeId": "230_crited", + "targetNodeId": "231_uninit"}, + {"id": "edge142", + "label": {"text": "next"}, + "sourceNodeId": "229_tailc", + "targetNodeId": "230_crited"}, + {"id": "edge143", + "label": {"text": "next"}, + "sourceNodeId": "228_sccopy", + "targetNodeId": "229_tailc"}, + {"id": "edge144", + "label": {"text": "next"}, + "sourceNodeId": "227_cddce", + "targetNodeId": "228_sccopy"}, + {"id": "edge145", + "label": {"text": "next"}, + "sourceNodeId": "226_store-merging", + "targetNodeId": "227_cddce"}, + {"id": "edge146", + "label": {"text": "next"}, + "sourceNodeId": "225_widening_mul", + "targetNodeId": "226_store-merging"}, + {"id": "edge147", + "label": {"text": "next"}, + "sourceNodeId": "224_fab", + "targetNodeId": "225_widening_mul"}, + {"id": "edge148", + "label": {"text": "next"}, + "sourceNodeId": "223_phiopt", + "targetNodeId": "224_fab"}, + {"id": "edge149", + "label": {"text": "next"}, + "sourceNodeId": "222_sink", + "targetNodeId": "223_phiopt"}, + {"id": "edge150", + "label": {"text": "next"}, + "sourceNodeId": "221_forwprop", + "targetNodeId": "222_sink"}, + {"id": "edge151", + "label": {"text": "next"}, + "sourceNodeId": "220_dce", + "targetNodeId": "221_forwprop"}, + {"id": "edge152", + "label": {"text": "next"}, + "sourceNodeId": "219_dse", + "targetNodeId": "220_dce"}, + {"id": "edge153", + "label": {"text": "next"}, + "sourceNodeId": "218_wrestrict", + "targetNodeId": "219_dse"}, + {"id": "edge154", + "label": {"text": "next"}, + "sourceNodeId": "217_ccp", + "targetNodeId": "218_wrestrict"}, + {"id": "edge155", + "label": {"text": "next"}, + "sourceNodeId": "216_vrp", + "targetNodeId": "217_ccp"}, + {"id": "edge156", + "label": {"text": "next"}, + "sourceNodeId": "215_threadfull", + "targetNodeId": "216_vrp"}, + {"id": "edge157", + "label": {"text": "next"}, + "sourceNodeId": "214_strlen", + "targetNodeId": "215_threadfull"}, + {"id": "edge158", + "label": {"text": "next"}, + "sourceNodeId": "213_dom", + "targetNodeId": "214_strlen"}, + {"id": "edge159", + "label": {"text": "next"}, + "sourceNodeId": "212_thread", + "targetNodeId": "213_dom"}, + {"id": "edge160", + "label": {"text": "next"}, + "sourceNodeId": "211_fre", + "targetNodeId": "212_thread"}, + {"id": "edge161", + "label": {"text": "next"}, + "sourceNodeId": "210_tracer", + "targetNodeId": "211_fre"}, + {"id": "edge162", + "label": {"text": "next"}, + "sourceNodeId": "209_split-paths", + "targetNodeId": "210_tracer"}, + {"id": "edge163", + "label": {"text": "next"}, + "sourceNodeId": "208_slsr", + "targetNodeId": "209_split-paths"}, + {"id": "edge164", + "label": {"text": "next"}, + "sourceNodeId": "207_reassoc", + "targetNodeId": "208_slsr"}, + {"id": "edge165", + "label": {"text": "next"}, + "sourceNodeId": "206_recip", + "targetNodeId": "207_reassoc"}, + {"id": "edge166", + "label": {"text": "next"}, + "sourceNodeId": "205_sincos", + "targetNodeId": "206_recip"}, + {"id": "edge167", + "label": {"text": "next"}, + "sourceNodeId": "204_switchlower", + "targetNodeId": "205_sincos"}, + {"id": "edge168", + "label": {"text": "next"}, + "sourceNodeId": "203_veclower2", + "targetNodeId": "204_switchlower"}, + {"id": "edge169", + "label": {"text": "next"}, + "sourceNodeId": "202_simduid", + "targetNodeId": "203_veclower2"}, + {"id": "edge170", + "label": {"text": "next"}, + "sourceNodeId": "200_no_loop", + "targetNodeId": "202_simduid"}, + {"id": "edge171", + "label": {"text": "next"}, + "sourceNodeId": "167_loop", + "targetNodeId": "200_no_loop"}, + {"id": "edge172", + "label": {"text": "next"}, + "sourceNodeId": "166_fix_loops", + "targetNodeId": "167_loop"}, + {"id": "edge173", + "label": {"text": "next"}, + "sourceNodeId": "165_dce", + "targetNodeId": "166_fix_loops"}, + {"id": "edge174", + "label": {"text": "next"}, + "sourceNodeId": "164_dse", + "targetNodeId": "165_dce"}, + {"id": "edge175", + "label": {"text": "next"}, + "sourceNodeId": "163_tsan", + "targetNodeId": "164_dse"}, + {"id": "edge176", + "label": {"text": "next"}, + "sourceNodeId": "162_asan", + "targetNodeId": "163_tsan"}, + {"id": "edge177", + "label": {"text": "next"}, + "sourceNodeId": "161_sancov", + "targetNodeId": "162_asan"}, + {"id": "edge178", + "label": {"text": "next"}, + "sourceNodeId": "160_sink", + "targetNodeId": "161_sancov"}, + {"id": "edge179", + "label": {"text": "next"}, + "sourceNodeId": "159_pre", + "targetNodeId": "160_sink"}, + {"id": "edge180", + "label": {"text": "next"}, + "sourceNodeId": "158_walloca", + "targetNodeId": "159_pre"}, + {"id": "edge181", + "label": {"text": "next"}, + "sourceNodeId": "157_lim", + "targetNodeId": "158_walloca"}, + {"id": "edge182", + "label": {"text": "next"}, + "sourceNodeId": "156_laddress", + "targetNodeId": "157_lim"}, + {"id": "edge183", + "label": {"text": "next"}, + "sourceNodeId": "155_bswap", + "targetNodeId": "156_laddress"}, + {"id": "edge184", + "label": {"text": "next"}, + "sourceNodeId": "154_pow", + "targetNodeId": "155_bswap"}, + {"id": "edge185", + "label": {"text": "next"}, + "sourceNodeId": "153_ccp", + "targetNodeId": "154_pow"}, + {"id": "edge186", + "label": {"text": "next"}, + "sourceNodeId": "152_phiopt", + "targetNodeId": "153_ccp"}, + {"id": "edge187", + "label": {"text": "next"}, + "sourceNodeId": "151_forwprop", + "targetNodeId": "152_phiopt"}, + {"id": "edge188", + "label": {"text": "next"}, + "sourceNodeId": "150_dce", + "targetNodeId": "151_forwprop"}, + {"id": "edge189", + "label": {"text": "next"}, + "sourceNodeId": "149_reassoc", + "targetNodeId": "150_dce"}, + {"id": "edge190", + "label": {"text": "next"}, + "sourceNodeId": "148_isolate-paths", + "targetNodeId": "149_reassoc"}, + {"id": "edge191", + "label": {"text": "next"}, + "sourceNodeId": "147_copyprop", + "targetNodeId": "148_isolate-paths"}, + {"id": "edge192", + "label": {"text": "next"}, + "sourceNodeId": "146_dom", + "targetNodeId": "147_copyprop"}, + {"id": "edge193", + "label": {"text": "next"}, + "sourceNodeId": "145_thread", + "targetNodeId": "146_dom"}, + {"id": "edge194", + "label": {"text": "next"}, + "sourceNodeId": "144_sra", + "targetNodeId": "145_thread"}, + {"id": "edge195", + "label": {"text": "next"}, + "sourceNodeId": "143_bitintlower", + "targetNodeId": "144_sra"}, + {"id": "edge196", + "label": {"text": "next"}, + "sourceNodeId": "142_cplxlower", + "targetNodeId": "143_bitintlower"}, + {"id": "edge197", + "label": {"text": "next"}, + "sourceNodeId": "141_ch", + "targetNodeId": "142_cplxlower"}, + {"id": "edge198", + "label": {"text": "next"}, + "sourceNodeId": "140_tailr", + "targetNodeId": "141_ch"}, + {"id": "edge199", + "label": {"text": "next"}, + "sourceNodeId": "139_phiopt", + "targetNodeId": "140_tailr"}, + {"id": "edge200", + "label": {"text": "next"}, + "sourceNodeId": "138_mergephi", + "targetNodeId": "139_phiopt"}, + {"id": "edge201", + "label": {"text": "next"}, + "sourceNodeId": "137_ifcombine", + "targetNodeId": "138_mergephi"}, + {"id": "edge202", + "label": {"text": "next"}, + "sourceNodeId": "136_copyprop", + "targetNodeId": "137_ifcombine"}, + {"id": "edge203", + "label": {"text": "next"}, + "sourceNodeId": "135_cselim", + "targetNodeId": "136_copyprop"}, + {"id": "edge204", + "label": {"text": "next"}, + "sourceNodeId": "134_cdce", + "targetNodeId": "135_cselim"}, + {"id": "edge205", + "label": {"text": "next"}, + "sourceNodeId": "133_stdarg", + "targetNodeId": "134_cdce"}, + {"id": "edge206", + "label": {"text": "next"}, + "sourceNodeId": "132_dce", + "targetNodeId": "133_stdarg"}, + {"id": "edge207", + "label": {"text": "next"}, + "sourceNodeId": "131_dse", + "targetNodeId": "132_dce"}, + {"id": "edge208", + "label": {"text": "next"}, + "sourceNodeId": "130_bounds", + "targetNodeId": "131_dse"}, + {"id": "edge209", + "label": {"text": "next"}, + "sourceNodeId": "129_vrp", + "targetNodeId": "130_bounds"}, + {"id": "edge210", + "label": {"text": "next"}, + "sourceNodeId": "128_threadfull", + "targetNodeId": "129_vrp"}, + {"id": "edge211", + "label": {"text": "next"}, + "sourceNodeId": "127_mergephi", + "targetNodeId": "128_threadfull"}, + {"id": "edge212", + "label": {"text": "next"}, + "sourceNodeId": "126_fre", + "targetNodeId": "127_mergephi"}, + {"id": "edge213", + "label": {"text": "next"}, + "sourceNodeId": "125_retslot", + "targetNodeId": "126_fre"}, + {"id": "edge214", + "label": {"text": "next"}, + "sourceNodeId": "124_alias", + "targetNodeId": "125_retslot"}, + {"id": "edge215", + "label": {"text": "next"}, + "sourceNodeId": "123_forwprop", + "targetNodeId": "124_alias"}, + {"id": "edge216", + "label": {"text": "next"}, + "sourceNodeId": "122_phiprop", + "targetNodeId": "123_forwprop"}, + {"id": "edge217", + "label": {"text": "next"}, + "sourceNodeId": "121_backprop", + "targetNodeId": "122_phiprop"}, + {"id": "edge218", + "label": {"text": "next"}, + "sourceNodeId": "120_cunrolli", + "targetNodeId": "121_backprop"}, + {"id": "edge219", + "label": {"text": "next"}, + "sourceNodeId": "119_rebuild_frequencies", + "targetNodeId": "120_cunrolli"}, + {"id": "edge220", + "label": {"text": "next"}, + "sourceNodeId": "118_waccess", + "targetNodeId": "119_rebuild_frequencies"}, + {"id": "edge221", + "label": {"text": "next"}, + "sourceNodeId": "117_post_ipa_warn", + "targetNodeId": "118_waccess"}, + {"id": "edge222", + "label": {"text": "next"}, + "sourceNodeId": "116_objsz", + "targetNodeId": "117_post_ipa_warn"}, + {"id": "edge223", + "label": {"text": "next"}, + "sourceNodeId": "115_ccp", + "targetNodeId": "116_objsz"}, + {"id": "edge224", + "label": {"text": "next"}, + "sourceNodeId": "*strip_predict_hints_0x101f27e0", + "targetNodeId": "115_ccp"}, + {"id": "edge225", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f2780", + "targetNodeId": "*strip_predict_hints_0x101f27e0"}, + {"id": "edge226", + "label": {"text": "sub"}, + "sourceNodeId": "*all_optimizations_0x101f2720", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f2780"}, + {"id": "edge227", + "label": {"text": "next"}, + "sourceNodeId": "251_uninit", + "targetNodeId": "252_uncprop"}, + {"id": "edge228", + "label": {"text": "next"}, + "sourceNodeId": "250_crited", + "targetNodeId": "251_uninit"}, + {"id": "edge229", + "label": {"text": "next"}, + "sourceNodeId": "249_tsan", + "targetNodeId": "250_crited"}, + {"id": "edge230", + "label": {"text": "next"}, + "sourceNodeId": "248_asan", + "targetNodeId": "249_tsan"}, + {"id": "edge231", + "label": {"text": "next"}, + "sourceNodeId": "247_sancov", + "targetNodeId": "248_asan"}, + {"id": "edge232", + "label": {"text": "next"}, + "sourceNodeId": "246_rebuild_frequencies", + "targetNodeId": "247_sancov"}, + {"id": "edge233", + "label": {"text": "next"}, + "sourceNodeId": "245_dce", + "targetNodeId": "246_rebuild_frequencies"}, + {"id": "edge234", + "label": {"text": "next"}, + "sourceNodeId": "244_copyprop", + "targetNodeId": "245_dce"}, + {"id": "edge235", + "label": {"text": "next"}, + "sourceNodeId": "243_strlen", + "targetNodeId": "244_copyprop"}, + {"id": "edge236", + "label": {"text": "next"}, + "sourceNodeId": "242_fab", + "targetNodeId": "243_strlen"}, + {"id": "edge237", + "label": {"text": "next"}, + "sourceNodeId": "241_objsz", + "targetNodeId": "242_fab"}, + {"id": "edge238", + "label": {"text": "next"}, + "sourceNodeId": "240_post_ipa_warn", + "targetNodeId": "241_objsz"}, + {"id": "edge239", + "label": {"text": "next"}, + "sourceNodeId": "239_ccp", + "targetNodeId": "240_post_ipa_warn"}, + {"id": "edge240", + "label": {"text": "next"}, + "sourceNodeId": "238_switchlower", + "targetNodeId": "239_ccp"}, + {"id": "edge241", + "label": {"text": "next"}, + "sourceNodeId": "237_veclower2", + "targetNodeId": "238_switchlower"}, + {"id": "edge242", + "label": {"text": "next"}, + "sourceNodeId": "236_bitintlower", + "targetNodeId": "237_veclower2"}, + {"id": "edge243", + "label": {"text": "next"}, + "sourceNodeId": "235_cplxlower", + "targetNodeId": "236_bitintlower"}, + {"id": "edge244", + "label": {"text": "next"}, + "sourceNodeId": "*strip_predict_hints_0x101f5bb0", + "targetNodeId": "235_cplxlower"}, + {"id": "edge245", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f5b50", + "targetNodeId": "*strip_predict_hints_0x101f5bb0"}, + {"id": "edge246", + "label": {"text": "sub"}, + "sourceNodeId": "*all_optimizations_g_0x101f5af0", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f5b50"}, + {"id": "edge247", + "label": {"text": "next"}, + "sourceNodeId": "255_tmmemopt", + "targetNodeId": "256_tmedge"}, + {"id": "edge248", + "label": {"text": "next"}, + "sourceNodeId": "254_tmmark", + "targetNodeId": "255_tmmemopt"}, + {"id": "edge249", + "label": {"text": "sub"}, + "sourceNodeId": "*tminit_0x101f6370", + "targetNodeId": "254_tmmark"}, + {"id": "edge250", + "label": {"text": "next"}, + "sourceNodeId": "299_loop2_doloop", + "targetNodeId": "300_loop2_done"}, + {"id": "edge251", + "label": {"text": "next"}, + "sourceNodeId": "298_loop2_unroll", + "targetNodeId": "299_loop2_doloop"}, + {"id": "edge252", + "label": {"text": "next"}, + "sourceNodeId": "297_loop2_invariant", + "targetNodeId": "298_loop2_unroll"}, + {"id": "edge253", + "label": {"text": "next"}, + "sourceNodeId": "296_loop2_init", + "targetNodeId": "297_loop2_invariant"}, + {"id": "edge254", + "label": {"text": "sub"}, + "sourceNodeId": "295_loop2", + "targetNodeId": "296_loop2_init"}, + {"id": "edge255", + "label": {"text": "next"}, + "sourceNodeId": "355_split4", + "targetNodeId": "356_stack"}, + {"id": "edge256", + "label": {"text": "sub"}, + "sourceNodeId": "*stack_regs_0x101f8d10", + "targetNodeId": "355_split4"}, + {"id": "edge257", + "label": {"text": "next"}, + "sourceNodeId": "354_sched2", + "targetNodeId": "*stack_regs_0x101f8d10"}, + {"id": "edge258", + "label": {"text": "next"}, + "sourceNodeId": "353_split3", + "targetNodeId": "354_sched2"}, + {"id": "edge259", + "label": {"text": "next"}, + "sourceNodeId": "*leaf_regs_0x101f8bf0", + "targetNodeId": "353_split3"}, + {"id": "edge260", + "label": {"text": "next"}, + "sourceNodeId": "352_bbro", + "targetNodeId": "*leaf_regs_0x101f8bf0"}, + {"id": "edge261", + "label": {"text": "next"}, + "sourceNodeId": "351_rtl_dce", + "targetNodeId": "352_bbro"}, + {"id": "edge262", + "label": {"text": "next"}, + "sourceNodeId": "350_cprop_hardreg", + "targetNodeId": "351_rtl_dce"}, + {"id": "edge263", + "label": {"text": "next"}, + "sourceNodeId": "349_fold_mem_offsets", + "targetNodeId": "350_cprop_hardreg"}, + {"id": "edge264", + "label": {"text": "next"}, + "sourceNodeId": "348_rnreg", + "targetNodeId": "349_fold_mem_offsets"}, + {"id": "edge265", + "label": {"text": "next"}, + "sourceNodeId": "347_ce3", + "targetNodeId": "348_rnreg"}, + {"id": "edge266", + "label": {"text": "next"}, + "sourceNodeId": "346_peephole2", + "targetNodeId": "347_ce3"}, + {"id": "edge267", + "label": {"text": "next"}, + "sourceNodeId": "345_sched_fusion", + "targetNodeId": "346_peephole2"}, + {"id": "edge268", + "label": {"text": "next"}, + "sourceNodeId": "344_compgotos", + "targetNodeId": "345_sched_fusion"}, + {"id": "edge269", + "label": {"text": "next"}, + "sourceNodeId": "343_jump2", + "targetNodeId": "344_compgotos"}, + {"id": "edge270", + "label": {"text": "next"}, + "sourceNodeId": "342_csa", + "targetNodeId": "343_jump2"}, + {"id": "edge271", + "label": {"text": "next"}, + "sourceNodeId": "341_dse2", + "targetNodeId": "342_csa"}, + {"id": "edge272", + "label": {"text": "next"}, + "sourceNodeId": "340_pro_and_epilogue", + "targetNodeId": "341_dse2"}, + {"id": "edge273", + "label": {"text": "next"}, + "sourceNodeId": "339_cmpelim", + "targetNodeId": "340_pro_and_epilogue"}, + {"id": "edge274", + "label": {"text": "next"}, + "sourceNodeId": "338_ree", + "targetNodeId": "339_cmpelim"}, + {"id": "edge275", + "label": {"text": "next"}, + "sourceNodeId": "337_split2", + "targetNodeId": "338_ree"}, + {"id": "edge276", + "label": {"text": "next"}, + "sourceNodeId": "336_gcse2", + "targetNodeId": "337_split2"}, + {"id": "edge277", + "label": {"text": "next"}, + "sourceNodeId": "335_late_combine", + "targetNodeId": "336_gcse2"}, + {"id": "edge278", + "label": {"text": "next"}, + "sourceNodeId": "334_vzeroupper", + "targetNodeId": "335_late_combine"}, + {"id": "edge279", + "label": {"text": "next"}, + "sourceNodeId": "333_postreload", + "targetNodeId": "334_vzeroupper"}, + {"id": "edge280", + "label": {"text": "sub"}, + "sourceNodeId": "*all-postreload_0x101f8410", + "targetNodeId": "333_postreload"}, + {"id": "edge281", + "label": {"text": "next"}, + "sourceNodeId": "370_dwarf2", + "targetNodeId": "371_final"}, + {"id": "edge282", + "label": {"text": "next"}, + "sourceNodeId": "369_nothrow", + "targetNodeId": "370_dwarf2"}, + {"id": "edge283", + "label": {"text": "next"}, + "sourceNodeId": "368_shorten", + "targetNodeId": "369_nothrow"}, + {"id": "edge284", + "label": {"text": "next"}, + "sourceNodeId": "367_align_tight_loops", + "targetNodeId": "368_shorten"}, + {"id": "edge285", + "label": {"text": "next"}, + "sourceNodeId": "366_endbr_and_patchable_area", + "targetNodeId": "367_align_tight_loops"}, + {"id": "edge286", + "label": {"text": "next"}, + "sourceNodeId": "365_eh_ranges", + "targetNodeId": "366_endbr_and_patchable_area"}, + {"id": "edge287", + "label": {"text": "next"}, + "sourceNodeId": "364_split5", + "targetNodeId": "365_eh_ranges"}, + {"id": "edge288", + "label": {"text": "next"}, + "sourceNodeId": "363_dbr", + "targetNodeId": "364_split5"}, + {"id": "edge289", + "label": {"text": "next"}, + "sourceNodeId": "362_barriers", + "targetNodeId": "363_dbr"}, + {"id": "edge290", + "label": {"text": "next"}, + "sourceNodeId": "361_mach", + "targetNodeId": "362_barriers"}, + {"id": "edge291", + "label": {"text": "next"}, + "sourceNodeId": "*free_cfg_0x101f9010", + "targetNodeId": "361_mach"}, + {"id": "edge292", + "label": {"text": "next"}, + "sourceNodeId": "360_vartrack", + "targetNodeId": "*free_cfg_0x101f9010"}, + {"id": "edge293", + "label": {"text": "next"}, + "sourceNodeId": "359_alignments", + "targetNodeId": "360_vartrack"}, + {"id": "edge294", + "label": {"text": "next"}, + "sourceNodeId": "358_zero_call_used_regs", + "targetNodeId": "359_alignments"}, + {"id": "edge295", + "label": {"text": "sub"}, + "sourceNodeId": "*all-late_compilation_0x101f8e90", + "targetNodeId": "358_zero_call_used_regs"}, + {"id": "edge296", + "label": {"text": "next"}, + "sourceNodeId": "*all-late_compilation_0x101f8e90", + "targetNodeId": "372_dfinish"}, + {"id": "edge297", + "label": {"text": "next"}, + "sourceNodeId": "357_late_pro_and_epilogue", + "targetNodeId": "*all-late_compilation_0x101f8e90"}, + {"id": "edge298", + "label": {"text": "next"}, + "sourceNodeId": "*all-postreload_0x101f8410", + "targetNodeId": "357_late_pro_and_epilogue"}, + {"id": "edge299", + "label": {"text": "next"}, + "sourceNodeId": "332_reload", + "targetNodeId": "*all-postreload_0x101f8410"}, + {"id": "edge300", + "label": {"text": "next"}, + "sourceNodeId": "331_ira", + "targetNodeId": "332_reload"}, + {"id": "edge301", + "label": {"text": "next"}, + "sourceNodeId": "330_early_remat", + "targetNodeId": "331_ira"}, + {"id": "edge302", + "label": {"text": "next"}, + "sourceNodeId": "329_avoid_store_forwarding", + "targetNodeId": "330_early_remat"}, + {"id": "edge303", + "label": {"text": "next"}, + "sourceNodeId": "328_sched1", + "targetNodeId": "329_avoid_store_forwarding"}, + {"id": "edge304", + "label": {"text": "next"}, + "sourceNodeId": "327_lr_shrinkage", + "targetNodeId": "328_sched1"}, + {"id": "edge305", + "label": {"text": "next"}, + "sourceNodeId": "326_sms", + "targetNodeId": "327_lr_shrinkage"}, + {"id": "edge306", + "label": {"text": "next"}, + "sourceNodeId": "325_asmcons", + "targetNodeId": "326_sms"}, + {"id": "edge307", + "label": {"text": "next"}, + "sourceNodeId": "324_mode_sw", + "targetNodeId": "325_asmcons"}, + {"id": "edge308", + "label": {"text": "next"}, + "sourceNodeId": "*stack_ptr_mod_0x101f8050", + "targetNodeId": "324_mode_sw"}, + {"id": "edge309", + "label": {"text": "next"}, + "sourceNodeId": "323_no-opt dfinit", + "targetNodeId": "*stack_ptr_mod_0x101f8050"}, + {"id": "edge310", + "label": {"text": "next"}, + "sourceNodeId": "322_subreg3", + "targetNodeId": "323_no-opt dfinit"}, + {"id": "edge311", + "label": {"text": "next"}, + "sourceNodeId": "321_split1", + "targetNodeId": "322_subreg3"}, + {"id": "edge312", + "label": {"text": "next"}, + "sourceNodeId": "320_outof_cfglayout", + "targetNodeId": "321_split1"}, + {"id": "edge313", + "label": {"text": "next"}, + "sourceNodeId": "319_bbpart", + "targetNodeId": "320_outof_cfglayout"}, + {"id": "edge314", + "label": {"text": "next"}, + "sourceNodeId": "318_jump_after_combine", + "targetNodeId": "319_bbpart"}, + {"id": "edge315", + "label": {"text": "next"}, + "sourceNodeId": "317_ce2", + "targetNodeId": "318_jump_after_combine"}, + {"id": "edge316", + "label": {"text": "next"}, + "sourceNodeId": "316_stv", + "targetNodeId": "317_ce2"}, + {"id": "edge317", + "label": {"text": "next"}, + "sourceNodeId": "315_rrvl", + "targetNodeId": "316_stv"}, + {"id": "edge318", + "label": {"text": "next"}, + "sourceNodeId": "314_rpad", + "targetNodeId": "315_rrvl"}, + {"id": "edge319", + "label": {"text": "next"}, + "sourceNodeId": "313_late_combine", + "targetNodeId": "314_rpad"}, + {"id": "edge320", + "label": {"text": "next"}, + "sourceNodeId": "312_combine", + "targetNodeId": "313_late_combine"}, + {"id": "edge321", + "label": {"text": "next"}, + "sourceNodeId": "311_ext_dce", + "targetNodeId": "312_combine"}, + {"id": "edge322", + "label": {"text": "next"}, + "sourceNodeId": "310_ud_dce", + "targetNodeId": "311_ext_dce"}, + {"id": "edge323", + "label": {"text": "next"}, + "sourceNodeId": "309_init-regs", + "targetNodeId": "310_ud_dce"}, + {"id": "edge324", + "label": {"text": "next"}, + "sourceNodeId": "308_auto_inc_dec", + "targetNodeId": "309_init-regs"}, + {"id": "edge325", + "label": {"text": "next"}, + "sourceNodeId": "307_fwprop2", + "targetNodeId": "308_auto_inc_dec"}, + {"id": "edge326", + "label": {"text": "next"}, + "sourceNodeId": "306_dse1", + "targetNodeId": "307_fwprop2"}, + {"id": "edge327", + "label": {"text": "next"}, + "sourceNodeId": "305_cse2", + "targetNodeId": "306_dse1"}, + {"id": "edge328", + "label": {"text": "next"}, + "sourceNodeId": "304_stv", + "targetNodeId": "305_cse2"}, + {"id": "edge329", + "label": {"text": "next"}, + "sourceNodeId": "303_cprop", + "targetNodeId": "304_stv"}, + {"id": "edge330", + "label": {"text": "next"}, + "sourceNodeId": "302_web", + "targetNodeId": "303_cprop"}, + {"id": "edge331", + "label": {"text": "next"}, + "sourceNodeId": "301_subreg2", + "targetNodeId": "302_web"}, + {"id": "edge332", + "label": {"text": "next"}, + "sourceNodeId": "295_loop2", + "targetNodeId": "301_subreg2"}, + {"id": "edge333", + "label": {"text": "next"}, + "sourceNodeId": "294_reginfo", + "targetNodeId": "295_loop2"}, + {"id": "edge334", + "label": {"text": "next"}, + "sourceNodeId": "293_apx_nfcvt", + "targetNodeId": "294_reginfo"}, + {"id": "edge335", + "label": {"text": "next"}, + "sourceNodeId": "292_ce1", + "targetNodeId": "293_apx_nfcvt"}, + {"id": "edge336", + "label": {"text": "next"}, + "sourceNodeId": "291_cse_local", + "targetNodeId": "292_ce1"}, + {"id": "edge337", + "label": {"text": "next"}, + "sourceNodeId": "290_store_motion", + "targetNodeId": "291_cse_local"}, + {"id": "edge338", + "label": {"text": "next"}, + "sourceNodeId": "289_cprop", + "targetNodeId": "290_store_motion"}, + {"id": "edge339", + "label": {"text": "next"}, + "sourceNodeId": "288_hardreg_pre", + "targetNodeId": "289_cprop"}, + {"id": "edge340", + "label": {"text": "next"}, + "sourceNodeId": "287_hoist", + "targetNodeId": "288_hardreg_pre"}, + {"id": "edge341", + "label": {"text": "next"}, + "sourceNodeId": "286_rtl pre", + "targetNodeId": "287_hoist"}, + {"id": "edge342", + "label": {"text": "next"}, + "sourceNodeId": "285_cprop", + "targetNodeId": "286_rtl pre"}, + {"id": "edge343", + "label": {"text": "next"}, + "sourceNodeId": "284_fwprop1", + "targetNodeId": "285_cprop"}, + {"id": "edge344", + "label": {"text": "next"}, + "sourceNodeId": "283_cse1", + "targetNodeId": "284_fwprop1"}, + {"id": "edge345", + "label": {"text": "next"}, + "sourceNodeId": "282_dfinit", + "targetNodeId": "283_cse1"}, + {"id": "edge346", + "label": {"text": "next"}, + "sourceNodeId": "281_subreg1", + "targetNodeId": "282_dfinit"}, + {"id": "edge347", + "label": {"text": "next"}, + "sourceNodeId": "280_jump", + "targetNodeId": "281_subreg1"}, + {"id": "edge348", + "label": {"text": "next"}, + "sourceNodeId": "279_into_cfglayout", + "targetNodeId": "280_jump"}, + {"id": "edge349", + "label": {"text": "next"}, + "sourceNodeId": "278_vregs", + "targetNodeId": "279_into_cfglayout"}, + {"id": "edge350", + "label": {"text": "sub"}, + "sourceNodeId": "*rest_of_compilation_0x101f6e90", + "targetNodeId": "278_vregs"}, + {"id": "edge351", + "label": {"text": "next"}, + "sourceNodeId": "*rest_of_compilation_0x101f6e90", + "targetNodeId": "*clean_state_0x101f9500"}, + {"id": "edge352", + "label": {"text": "next"}, + "sourceNodeId": "277_expand", + "targetNodeId": "*rest_of_compilation_0x101f6e90"}, + {"id": "edge353", + "label": {"text": "next"}, + "sourceNodeId": "*warn_function_noreturn_0x101f6dd0", + "targetNodeId": "277_expand"}, + {"id": "edge354", + "label": {"text": "next"}, + "sourceNodeId": "276_optimized", + "targetNodeId": "*warn_function_noreturn_0x101f6dd0"}, + {"id": "edge355", + "label": {"text": "next"}, + "sourceNodeId": "275_waccess", + "targetNodeId": "276_optimized"}, + {"id": "edge356", + "label": {"text": "next"}, + "sourceNodeId": "274_hardcmp", + "targetNodeId": "275_waccess"}, + {"id": "edge357", + "label": {"text": "next"}, + "sourceNodeId": "273_hardcbr", + "targetNodeId": "274_hardcmp"}, + {"id": "edge358", + "label": {"text": "next"}, + "sourceNodeId": "272_isel", + "targetNodeId": "273_hardcbr"}, + {"id": "edge359", + "label": {"text": "next"}, + "sourceNodeId": "271_nrv", + "targetNodeId": "272_isel"}, + {"id": "edge360", + "label": {"text": "next"}, + "sourceNodeId": "270_resx", + "targetNodeId": "271_nrv"}, + {"id": "edge361", + "label": {"text": "next"}, + "sourceNodeId": "269_ehcleanup", + "targetNodeId": "270_resx"}, + {"id": "edge362", + "label": {"text": "next"}, + "sourceNodeId": "268_sanopt", + "targetNodeId": "269_ehcleanup"}, + {"id": "edge363", + "label": {"text": "next"}, + "sourceNodeId": "267_musttail", + "targetNodeId": "268_sanopt"}, + {"id": "edge364", + "label": {"text": "next"}, + "sourceNodeId": "266_tsan0", + "targetNodeId": "267_musttail"}, + {"id": "edge365", + "label": {"text": "next"}, + "sourceNodeId": "265_asan0", + "targetNodeId": "266_tsan0"}, + {"id": "edge366", + "label": {"text": "next"}, + "sourceNodeId": "264_switchlower_O0", + "targetNodeId": "265_asan0"}, + {"id": "edge367", + "label": {"text": "next"}, + "sourceNodeId": "263_sancov_O0", + "targetNodeId": "264_switchlower_O0"}, + {"id": "edge368", + "label": {"text": "next"}, + "sourceNodeId": "262_bitintlower0", + "targetNodeId": "263_sancov_O0"}, + {"id": "edge369", + "label": {"text": "next"}, + "sourceNodeId": "261_cplxlower0", + "targetNodeId": "262_bitintlower0"}, + {"id": "edge370", + "label": {"text": "next"}, + "sourceNodeId": "260_veclower", + "targetNodeId": "261_cplxlower0"}, + {"id": "edge371", + "label": {"text": "next"}, + "sourceNodeId": "259_lower_vaarg", + "targetNodeId": "260_veclower"}, + {"id": "edge372", + "label": {"text": "next"}, + "sourceNodeId": "258_vtable-verify", + "targetNodeId": "259_lower_vaarg"}, + {"id": "edge373", + "label": {"text": "next"}, + "sourceNodeId": "257_simduid", + "targetNodeId": "258_vtable-verify"}, + {"id": "edge374", + "label": {"text": "next"}, + "sourceNodeId": "*tminit_0x101f6370", + "targetNodeId": "257_simduid"}, + {"id": "edge375", + "label": {"text": "next"}, + "sourceNodeId": "253_assumptions", + "targetNodeId": "*tminit_0x101f6370"}, + {"id": "edge376", + "label": {"text": "next"}, + "sourceNodeId": "*all_optimizations_g_0x101f5af0", + "targetNodeId": "253_assumptions"}, + {"id": "edge377", + "label": {"text": "next"}, + "sourceNodeId": "*all_optimizations_0x101f2720", + "targetNodeId": "*all_optimizations_g_0x101f5af0"}, + {"id": "edge378", + "label": {"text": "next"}, + "sourceNodeId": "114_hardcfr", + "targetNodeId": "*all_optimizations_0x101f2720"}, + {"id": "edge379", + "label": {"text": "next"}, + "sourceNodeId": "113_adjust_alignment", + "targetNodeId": "114_hardcfr"}, + {"id": "edge380", + "label": {"text": "next"}, + "sourceNodeId": "112_omptargetlink", + "targetNodeId": "113_adjust_alignment"}, + {"id": "edge381", + "label": {"text": "next"}, + "sourceNodeId": "111_ompdevlow", + "targetNodeId": "112_omptargetlink"}, + {"id": "edge382", + "label": {"text": "next"}, + "sourceNodeId": "110_oaccdevlow", + "targetNodeId": "111_ompdevlow"}, + {"id": "edge383", + "label": {"text": "next"}, + "sourceNodeId": "109_omp_oacc_neuter_broadcast", + "targetNodeId": "110_oaccdevlow"}, + {"id": "edge384", + "label": {"text": "next"}, + "sourceNodeId": "108_oaccloops", + "targetNodeId": "109_omp_oacc_neuter_broadcast"}, + {"id": "edge385", + "label": {"text": "next"}, + "sourceNodeId": "107_ehdisp", + "targetNodeId": "108_oaccloops"}, + {"id": "edge386", + "label": {"text": "next"}, + "sourceNodeId": "106_fixup_cfg", + "targetNodeId": "107_ehdisp"}]}]}]} + +/* { dg-begin-multiline-output "" } +In function 'test_graphs': +/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c:8:3: error: this is a placeholder error, with graphs + { dg-end-multiline-output "" } */ + +/* Use a Python script to verify various properties about the generated + .html file: + { dg-final { run-html-pytest graphs.sarif "2.1.0-valid/graphs-check-html.py" } } */ + +/* Use a Python script to verify various properties about the *generated* + .sarif file: + { dg-final { run-sarif-pytest graphs.roundtrip "2.1.0-valid/graphs-check-sarif-roundtrip.py" } } */ -- 2.47.2