Line data Source code
1 : /* Converting directed graphs to dot.
2 : Copyright (C) 2025 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #define INCLUDE_ALGORITHM
22 : #define INCLUDE_MAP
23 : #define INCLUDE_SET
24 : #define INCLUDE_STRING
25 : #define INCLUDE_VECTOR
26 : #include "config.h"
27 : #include "system.h"
28 : #include "coretypes.h"
29 :
30 : #include "graphviz.h"
31 : #include "xml.h"
32 : #include "xml-printer.h"
33 : #include "diagnostics/digraphs.h"
34 : #include "diagnostics/digraphs-to-dot.h"
35 : #include "diagnostics/sarif-sink.h"
36 :
37 : #include "selftest.h"
38 :
39 : namespace diagnostics {
40 : namespace digraphs {
41 : namespace to_dot {
42 :
43 : using digraph = diagnostics::digraphs::digraph;
44 : using digraph_node = diagnostics::digraphs::node;
45 : using digraph_edge = diagnostics::digraphs::edge;
46 :
47 : // class conversion_to_dot
48 :
49 : std::unique_ptr<dot::graph>
50 56 : converter::make_dot_graph_from_diagnostic_graph (const digraph &input_graph)
51 : {
52 56 : auto output_graph = std::make_unique<dot::graph> ();
53 :
54 56 : if (const char *description = input_graph.get_description ())
55 104 : output_graph->m_stmt_list.add_attr (dot::id ("label"),
56 104 : dot::id (description));
57 :
58 56 : const int num_nodes = input_graph.get_num_nodes ();
59 56 : const int num_edges = input_graph.get_num_edges ();
60 :
61 : /* Determine which nodes have in-edges and out-edges. */
62 722 : for (int i = 0; i < num_edges; ++i)
63 : {
64 666 : const digraph_edge &input_edge = input_graph.get_edge (i);
65 666 : m_nodes_with_edges.insert (&input_edge.get_src_node ());
66 666 : m_nodes_with_edges.insert (&input_edge.get_dst_node ());
67 : }
68 :
69 118 : for (int i = 0; i < num_nodes; ++i)
70 : {
71 62 : const digraph_node &input_node = input_graph.get_node (i);
72 62 : auto dot_node_stmt = make_dot_node_from_digraph_node (input_node);
73 62 : output_graph->m_stmt_list.add_stmt (std::move (dot_node_stmt));
74 62 : }
75 :
76 722 : for (int i = 0; i < num_edges; ++i)
77 : {
78 666 : const digraph_edge &input_edge = input_graph.get_edge (i);
79 666 : auto dot_edge_stmt = make_dot_edge_from_digraph_edge (input_edge);
80 666 : output_graph->m_stmt_list.add_stmt (std::move (dot_edge_stmt));
81 666 : }
82 :
83 56 : return output_graph;
84 : }
85 :
86 : std::unique_ptr<dot::stmt>
87 733 : converter::
88 : make_dot_node_from_digraph_node (const diagnostics::digraphs::node &input_node)
89 : {
90 733 : dot::id dot_id (get_dot_id_for_node (input_node));
91 :
92 : /* For now, we can only do either edges or children, not both
93 : ...but see https://graphviz.org/docs/attrs/compound/ */
94 :
95 733 : if (has_edges_p (input_node))
96 : {
97 677 : auto output_node
98 677 : = std::make_unique<dot::node_stmt> (std::move (dot_id));
99 677 : m_node_map[&input_node] = output_node.get ();
100 677 : if (const char *label = input_node.get_label ())
101 401 : output_node->set_label (dot::id (label));
102 677 : add_any_node_attrs (input_node, *output_node);
103 677 : return output_node;
104 677 : }
105 : else
106 : {
107 56 : auto output_node = std::make_unique<dot::subgraph> (std::move (dot_id));
108 56 : m_node_map[&input_node] = output_node.get ();
109 56 : if (const char *label = input_node.get_label ())
110 5 : output_node->add_attr (dot::id ("label"), dot::id (label));
111 56 : add_any_subgraph_attrs (input_node, *output_node);
112 56 : const int num_children = input_node.get_num_children ();
113 727 : for (int i = 0; i < num_children; ++i)
114 : {
115 671 : const digraph_node &input_child = input_node.get_child (i);
116 671 : auto dot_child_stmt = make_dot_node_from_digraph_node (input_child);
117 671 : output_node->m_stmt_list.add_stmt (std::move (dot_child_stmt));
118 671 : }
119 56 : return output_node;
120 56 : }
121 733 : }
122 :
123 : std::unique_ptr<dot::edge_stmt>
124 666 : converter::
125 : make_dot_edge_from_digraph_edge (const digraph_edge &input_edge)
126 : {
127 666 : const digraph_node &src_dnode = input_edge.get_src_node ();
128 666 : const digraph_node &dst_dnode = input_edge.get_dst_node ();
129 666 : auto output_edge
130 1332 : = std::make_unique<dot::edge_stmt> (get_node_id_for_node (src_dnode),
131 1998 : get_node_id_for_node (dst_dnode));
132 666 : if (const char *label = input_edge.get_label ())
133 396 : output_edge->set_label (dot::id (label));
134 666 : add_any_edge_attrs (input_edge, *output_edge);
135 666 : return output_edge;
136 : }
137 :
138 : dot::id
139 2155 : converter::get_dot_id_for_node (const digraph_node &input_node)
140 : {
141 2155 : if (has_edges_p (input_node))
142 4198 : return input_node.get_id ();
143 : else
144 112 : return std::string ("cluster_") + input_node.get_id ();
145 : }
146 :
147 : dot::node_id
148 1422 : converter::get_node_id_for_node (const digraph_node &input_node,
149 : const char *compass_point)
150 : {
151 1422 : dot::id id = get_dot_id_for_node (input_node);
152 1422 : if (compass_point)
153 : {
154 90 : enum dot::compass_pt pt;
155 90 : if (dot::get_compass_pt_from_string (compass_point, pt))
156 90 : return dot::node_id (id, pt);
157 : }
158 1332 : return dot::node_id (id);
159 1422 : }
160 :
161 : bool
162 2888 : converter::has_edges_p (const digraph_node &input_node)
163 : {
164 2888 : return m_nodes_with_edges.find (&input_node) != m_nodes_with_edges.end ();
165 : }
166 :
167 : void
168 11 : converter::add_any_subgraph_attrs (const digraph_node &/*input_node*/,
169 : dot::subgraph &/*output_subgraph*/)
170 : {
171 : // No-op
172 11 : }
173 :
174 : void
175 407 : converter::add_any_node_attrs (const digraph_node &/*input_node*/,
176 : dot::node_stmt &/*output_node*/)
177 : {
178 : // No-op
179 407 : }
180 :
181 : void
182 396 : converter::add_any_edge_attrs (const digraph_edge &/*input_edge*/,
183 : dot::edge_stmt &/*output_edge*/)
184 : {
185 : // No-op
186 396 : }
187 :
188 : std::unique_ptr<converter>
189 56 : converter::make (const diagnostics::digraphs::digraph &dg)
190 : {
191 56 : if (const char *graph_kind = dg.get_graph_kind ())
192 : {
193 : // Try to find a suitable converter subclass and use it
194 45 : if (strcmp (graph_kind, "cfg") == 0)
195 45 : return make_converter_from_cfg ();
196 : }
197 11 : return std::make_unique<converter> ();
198 : }
199 :
200 : } // namespace to_dot
201 : } // namespace digraphs
202 : } // namespace diagnostics
|