Line data Source code
1 : /* Emit optimization information as JSON files.
2 : Copyright (C) 2018-2026 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 under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : 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 : #include "config.h"
22 : #include "system.h"
23 : #include "coretypes.h"
24 :
25 : #include "backend.h"
26 : #include "tree.h"
27 : #include "gimple.h"
28 : #include "diagnostic-core.h"
29 :
30 : #include "profile.h"
31 : #include "output.h"
32 : #include "tree-pass.h"
33 :
34 : #include "optinfo.h"
35 : #include "optinfo-emit-json.h"
36 : #include "json.h"
37 : #include "pretty-print.h"
38 : #include "tree-pretty-print.h"
39 : #include "gimple-pretty-print.h"
40 : #include "cgraph.h"
41 :
42 : #include "langhooks.h"
43 : #include "version.h"
44 : #include "context.h"
45 : #include "pass_manager.h"
46 : #include "selftest.h"
47 : #include "dump-context.h"
48 : #include <zlib.h>
49 :
50 : /* optrecord_json_writer's ctor. Populate the top-level parts of the
51 : in-memory JSON representation. */
52 :
53 1288 : optrecord_json_writer::optrecord_json_writer ()
54 1288 : : m_root_tuple (nullptr), m_scopes ()
55 : {
56 1288 : m_root_tuple = std::make_unique<json::array> ();
57 :
58 : /* Populate with metadata; compare with toplev.cc: print_version. */
59 1288 : auto metadata = std::make_unique<json::object> ();
60 1288 : metadata->set_string ("format", "1");
61 1288 : auto generator = std::make_unique<json::object> ();
62 1288 : generator->set_string ("name", lang_hooks.name);
63 1288 : generator->set_string ("pkgversion", pkgversion_string);
64 1288 : generator->set_string ("version", version_string);
65 : /* TARGET_NAME is passed in by the Makefile. */
66 1288 : generator->set_string ("target", TARGET_NAME);
67 1288 : metadata->set<json::object> ("generator", std::move (generator));
68 1288 : m_root_tuple->append<json::object> (std::move (metadata));
69 :
70 : /* TODO: capture command-line?
71 : see gen_producer_string in dwarf2out.cc (currently static). */
72 :
73 : /* TODO: capture "any plugins?" flag (or the plugins themselves). */
74 :
75 1288 : auto passes = std::make_unique<json::array> ();
76 :
77 : /* Call add_pass_list for all of the pass lists. */
78 1288 : {
79 : #define DEF_PASS_LIST(LIST) \
80 : add_pass_list (passes.get (), g->get_passes ()->LIST);
81 1288 : GCC_PASS_LISTS
82 : #undef DEF_PASS_LIST
83 : }
84 1288 : m_root_tuple->append<json::array> (std::move (passes));
85 :
86 1288 : auto records = std::make_unique<json::array> ();
87 1288 : m_scopes.safe_push (records.get ());
88 1288 : m_root_tuple->append (std::move (records));
89 1288 : }
90 :
91 : /* Choose an appropriate filename, and write the saved records to it. */
92 :
93 : void
94 41 : optrecord_json_writer::write () const
95 : {
96 41 : pretty_printer pp;
97 41 : m_root_tuple->print (&pp, false);
98 :
99 41 : bool emitted_error = false;
100 41 : char *filename = concat (dump_base_name, ".opt-record.json.gz", nullptr);
101 41 : gzFile outfile = gzopen (filename, "w");
102 41 : if (outfile == nullptr)
103 : {
104 0 : error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
105 : filename); // FIXME: more info?
106 0 : goto cleanup;
107 : }
108 :
109 41 : if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
110 : {
111 0 : int tmp;
112 0 : error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
113 : filename, gzerror (outfile, &tmp));
114 0 : emitted_error = true;
115 : }
116 :
117 41 : cleanup:
118 41 : if (outfile)
119 41 : if (gzclose (outfile) != Z_OK)
120 0 : if (!emitted_error)
121 0 : error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
122 : filename);
123 :
124 41 : free (filename);
125 41 : }
126 :
127 : /* Add a record for OPTINFO to the queue of records to be written. */
128 :
129 : void
130 44714 : optrecord_json_writer::add_record (const optinfo &optinfo)
131 : {
132 44714 : auto obj = optinfo_to_json (optinfo);
133 44714 : auto borrowed_obj = obj.get ();
134 :
135 44714 : add_record (std::move (obj));
136 :
137 : /* Potentially push the scope. */
138 44714 : if (optinfo.get_kind () == optinfo::kind::scope)
139 : {
140 3958 : auto children = std::make_unique<json::array> ();
141 3958 : m_scopes.safe_push (children.get ());
142 3958 : borrowed_obj->set<json::array> ("children", std::move (children));
143 3958 : }
144 44714 : }
145 :
146 : /* Private methods of optrecord_json_writer. */
147 :
148 : /* Add record OBJ to the innermost scope. */
149 :
150 : void
151 44714 : optrecord_json_writer::add_record (std::unique_ptr<json::object> obj)
152 : {
153 : /* Add to innermost scope. */
154 44714 : gcc_assert (m_scopes.length () > 0);
155 44714 : m_scopes[m_scopes.length () - 1]->append<json::object> (std::move (obj));
156 44714 : }
157 :
158 : /* Pop the innermost scope. */
159 :
160 : void
161 3958 : optrecord_json_writer::pop_scope ()
162 : {
163 3958 : m_scopes.pop ();
164 :
165 : /* We should never pop the top-level records array. */
166 3958 : gcc_assert (m_scopes.length () > 0);
167 3958 : }
168 :
169 : /* Create a JSON object representing LOC. */
170 :
171 : std::unique_ptr<json::object>
172 44718 : optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
173 : {
174 44718 : auto obj = std::make_unique<json::object> ();
175 44718 : obj->set_string ("file", loc.m_file);
176 44718 : obj->set_integer ("line", loc.m_line);
177 44718 : if (loc.m_function)
178 44718 : obj->set_string ("function", loc.m_function);
179 44718 : return obj;
180 : }
181 :
182 : /* Create a JSON object representing LOC. */
183 :
184 : std::unique_ptr<json::object>
185 36722 : optrecord_json_writer::location_to_json (location_t loc)
186 : {
187 36722 : gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
188 36722 : expanded_location exploc = expand_location (loc);
189 36722 : auto obj = std::make_unique<json::object> ();
190 36722 : obj->set_string ("file", exploc.file);
191 36722 : obj->set_integer ("line", exploc.line);
192 36722 : obj->set_integer ("column", exploc.column);
193 36722 : return obj;
194 : }
195 :
196 : /* Create a JSON object representing COUNT. */
197 :
198 : std::unique_ptr<json::object>
199 44658 : optrecord_json_writer::profile_count_to_json (profile_count count)
200 : {
201 44658 : auto obj = std::make_unique<json::object> ();
202 44658 : obj->set_integer ("value", count.to_gcov_type ());
203 44658 : obj->set_string ("quality", profile_quality_as_string (count.quality ()));
204 44658 : return obj;
205 : }
206 :
207 : /* Get a string for use when referring to PASS in the saved optimization
208 : records. */
209 :
210 : std::unique_ptr<json::string>
211 549658 : optrecord_json_writer::get_id_value_for_pass (const opt_pass &pass)
212 : {
213 549658 : pretty_printer pp;
214 : /* this is host-dependent, but will be consistent for a given host. */
215 549658 : pp_pointer (&pp, static_cast<const void *> (&pass));
216 549658 : return std::make_unique<json::string> (pp_formatted_text (&pp));
217 549658 : }
218 :
219 : /* Create a JSON object representing PASS. */
220 :
221 : std::unique_ptr<json::object>
222 507472 : optrecord_json_writer::pass_to_json (const opt_pass &pass)
223 : {
224 507472 : auto obj = std::make_unique<json::object> ();
225 507472 : const char *type = nullptr;
226 507472 : switch (pass.type)
227 : {
228 0 : default:
229 0 : gcc_unreachable ();
230 : case GIMPLE_PASS:
231 : type = "gimple";
232 : break;
233 136528 : case RTL_PASS:
234 136528 : type = "rtl";
235 136528 : break;
236 28336 : case SIMPLE_IPA_PASS:
237 28336 : type = "simple_ipa";
238 28336 : break;
239 24472 : case IPA_PASS:
240 24472 : type = "ipa";
241 24472 : break;
242 : }
243 507472 : obj->set<json::string> ("id", get_id_value_for_pass (pass));
244 507472 : obj->set_string ("type", type);
245 507472 : obj->set_string ("name", pass.name);
246 : /* Represent the optgroup flags as an array. */
247 507472 : {
248 507472 : auto optgroups = std::make_unique<json::array> ();
249 3552304 : for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
250 3552304 : optgroup->name != nullptr; optgroup++)
251 3044832 : if (optgroup->value != OPTGROUP_ALL
252 2537360 : && (pass.optinfo_flags & optgroup->value))
253 91448 : optgroups->append_string (optgroup->name);
254 507472 : obj->set<json::array> ("optgroups", std::move (optgroups));
255 507472 : }
256 507472 : obj->set_integer ("num", pass.static_pass_number);
257 507472 : return obj;
258 : }
259 :
260 : /* Create a JSON array for LOC representing the chain of inlining
261 : locations.
262 : Compare with lhd_print_error_function and cp_print_error_function. */
263 :
264 : std::unique_ptr<json::array>
265 29588 : optrecord_json_writer::inlining_chain_to_json (location_t loc)
266 : {
267 29588 : auto array = std::make_unique<json::array> ();
268 :
269 29588 : tree abstract_origin = LOCATION_BLOCK (loc);
270 :
271 58005 : while (abstract_origin)
272 : {
273 28417 : location_t *locus;
274 28417 : tree block = abstract_origin;
275 :
276 28417 : locus = &BLOCK_SOURCE_LOCATION (block);
277 28417 : tree fndecl = nullptr;
278 28417 : block = BLOCK_SUPERCONTEXT (block);
279 58275 : while (block && TREE_CODE (block) == BLOCK
280 43499 : && BLOCK_ABSTRACT_ORIGIN (block))
281 : {
282 2945 : tree ao = BLOCK_ABSTRACT_ORIGIN (block);
283 2945 : if (TREE_CODE (ao) == FUNCTION_DECL)
284 : {
285 : fndecl = ao;
286 : break;
287 : }
288 1441 : else if (TREE_CODE (ao) != BLOCK)
289 : break;
290 :
291 1441 : block = BLOCK_SUPERCONTEXT (block);
292 : }
293 28417 : if (fndecl)
294 : abstract_origin = block;
295 : else
296 : {
297 50262 : while (block && TREE_CODE (block) == BLOCK)
298 23349 : block = BLOCK_SUPERCONTEXT (block);
299 :
300 26913 : if (block && TREE_CODE (block) == FUNCTION_DECL)
301 : fndecl = block;
302 : abstract_origin = nullptr;
303 : }
304 : if (fndecl)
305 : {
306 28417 : auto obj = std::make_unique<json::object> ();
307 28417 : const char *printable_name
308 28417 : = lang_hooks.decl_printable_name (fndecl, 2);
309 28417 : obj->set_string ("fndecl", printable_name);
310 28417 : if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
311 1504 : obj->set<json::object> ("site", location_to_json (*locus));
312 28417 : array->append<json::object> (std::move (obj));
313 28417 : }
314 : }
315 :
316 29588 : return array;
317 : }
318 :
319 : /* Create a JSON object representing OPTINFO. */
320 :
321 : std::unique_ptr<json::object>
322 44718 : optrecord_json_writer::optinfo_to_json (const optinfo &optinfo)
323 : {
324 44718 : auto obj = std::make_unique<json::object> ();
325 :
326 44718 : obj->set<json::object>
327 44718 : ("impl_location",
328 44718 : impl_location_to_json (optinfo.get_impl_location ()));
329 :
330 44718 : const char *kind_str = optinfo::kind_to_string (optinfo.get_kind ());
331 44718 : obj->set_string ("kind", kind_str);
332 44718 : auto message = std::make_unique<json::array> ();
333 152903 : for (unsigned i = 0; i < optinfo.num_items (); i++)
334 : {
335 108185 : const optinfo_item *item = optinfo.get_item (i);
336 108185 : switch (item->get_kind ())
337 : {
338 0 : default:
339 0 : gcc_unreachable ();
340 78455 : case optinfo_item::kind::text:
341 78455 : {
342 78455 : message->append_string (item->get_text ());
343 : }
344 78455 : break;
345 10451 : case optinfo_item::kind::tree:
346 10451 : {
347 10451 : auto json_item = std::make_unique<json::object> ();
348 10451 : json_item->set_string ("expr", item->get_text ());
349 :
350 : /* Capture any location for the node. */
351 10451 : if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
352 0 : json_item->set<json::object>
353 0 : ("location",
354 0 : location_to_json (item->get_location ()));
355 :
356 10451 : message->append<json::object> (std::move (json_item));
357 10451 : }
358 10451 : break;
359 19017 : case optinfo_item::kind::gimple:
360 19017 : {
361 19017 : auto json_item = std::make_unique<json::object> ();
362 19017 : json_item->set_string ("stmt", item->get_text ());
363 :
364 : /* Capture any location for the stmt. */
365 19017 : if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
366 6068 : json_item->set<json::object>
367 6068 : ("location",
368 6068 : location_to_json (item->get_location ()));
369 :
370 19017 : message->append<json::object> (std::move (json_item));
371 19017 : }
372 19017 : break;
373 262 : case optinfo_item::kind::symtab_node:
374 262 : {
375 262 : auto json_item = std::make_unique<json::object> ();
376 262 : json_item->set_string ("symtab_node", item->get_text ());
377 :
378 : /* Capture any location for the node. */
379 262 : if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
380 262 : json_item->set<json::object>
381 262 : ("location",
382 262 : location_to_json (item->get_location ()));
383 262 : message->append<json::object> (std::move (json_item));
384 262 : }
385 262 : break;
386 : }
387 : }
388 44718 : obj->set<json::array> ("message", std::move (message));
389 :
390 44718 : if (auto pass = optinfo.get_pass ())
391 42186 : obj->set<json::string> ("pass",
392 42186 : get_id_value_for_pass (*pass));
393 :
394 44718 : profile_count count = optinfo.get_count ();
395 44718 : if (count.initialized_p ())
396 44658 : obj->set<json::object> ("count", profile_count_to_json (count));
397 :
398 : /* Record any location, handling the case where of an UNKNOWN_LOCATION
399 : within an inlined block. */
400 44718 : location_t loc = optinfo.get_location_t ();
401 44718 : if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
402 : {
403 : // TOOD: record the location (just caret for now)
404 : // TODO: start/finish also?
405 28888 : obj->set<json::object> ("location", location_to_json (loc));
406 : }
407 :
408 44718 : if (current_function_decl)
409 : {
410 42104 : const char *fnname
411 42104 : = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
412 42104 : obj->set_string ("function", fnname);
413 : }
414 :
415 44718 : if (loc != UNKNOWN_LOCATION)
416 29588 : obj->set<json::array> ("inlining_chain", inlining_chain_to_json (loc));
417 :
418 89436 : return obj;
419 44718 : }
420 :
421 : /* Add a json description of PASS and its siblings to ARR, recursing into
422 : child passes (adding their descriptions within a "children" array). */
423 :
424 : void
425 33488 : optrecord_json_writer::add_pass_list (json::array *arr,
426 : const opt_pass *pass)
427 : {
428 33488 : gcc_assert (pass);
429 507472 : do
430 : {
431 507472 : auto pass_obj = pass_to_json (*pass);
432 507472 : if (pass->sub)
433 : {
434 27048 : auto child_arr = std::make_unique<json::array> ();
435 27048 : add_pass_list (child_arr.get (), pass->sub);
436 27048 : pass_obj->set ("children", std::move (child_arr));
437 27048 : }
438 507472 : arr->append (std::move (pass_obj));
439 507472 : pass = pass->next;
440 507472 : }
441 507472 : while (pass);
442 33488 : }
443 :
444 : #if CHECKING_P
445 :
446 : namespace selftest {
447 :
448 : /* Verify that we can build a JSON optimization record from dump_*
449 : calls. */
450 :
451 : static void
452 4 : test_building_json_from_dump_calls ()
453 : {
454 4 : temp_dump_context tmp (true, true, MSG_NOTE);
455 4 : dump_user_location_t loc;
456 4 : dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
457 4 : dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
458 4 : optinfo *info = tmp.get_pending_optinfo ();
459 4 : ASSERT_TRUE (info != nullptr);
460 4 : ASSERT_EQ (info->num_items (), 2);
461 :
462 4 : optrecord_json_writer writer;
463 4 : auto json_obj = writer.optinfo_to_json (*info);
464 4 : ASSERT_TRUE (json_obj != nullptr);
465 :
466 : /* Verify that the json is sane. */
467 4 : pretty_printer pp;
468 4 : json_obj->print (&pp, false);
469 4 : const char *json_str = pp_formatted_text (&pp);
470 4 : ASSERT_STR_CONTAINS (json_str, "impl_location");
471 4 : ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
472 4 : ASSERT_STR_CONTAINS (json_str,
473 : "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
474 8 : }
475 :
476 : /* Run all of the selftests within this file. */
477 :
478 : void
479 4 : optinfo_emit_json_cc_tests ()
480 : {
481 4 : test_building_json_from_dump_calls ();
482 4 : }
483 :
484 : } // namespace selftest
485 :
486 : #endif /* CHECKING_P */
|