]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/optinfo-emit-json.cc
Add "-fsave-optimization-record"
[thirdparty/gcc.git] / gcc / optinfo-emit-json.cc
1 /* Emit optimization information as JSON files.
2 Copyright (C) 2018 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
49 /* A class for writing out optimization records in JSON format. */
50
51 class optrecord_json_writer
52 {
53 public:
54 optrecord_json_writer ();
55 ~optrecord_json_writer ();
56 void write () const;
57 void add_record (const optinfo *optinfo);
58 void pop_scope ();
59
60 void add_record (json::object *obj);
61 json::object *impl_location_to_json (dump_impl_location_t loc);
62 json::object *location_to_json (location_t loc);
63 json::object *profile_count_to_json (profile_count count);
64 json::string *get_id_value_for_pass (opt_pass *pass);
65 json::object *pass_to_json (opt_pass *pass);
66 json::value *inlining_chain_to_json (location_t loc);
67 json::object *optinfo_to_json (const optinfo *optinfo);
68 void add_pass_list (json::array *arr, opt_pass *pass);
69
70 private:
71 /* The root value for the JSON file.
72 Currently the JSON values are stored in memory, and flushed when the
73 compiler exits. It would probably be better to simply write out
74 the JSON as we go. */
75 json::array *m_root_tuple;
76
77 /* The currently open scopes, for expressing nested optimization records. */
78 vec<json::array *> m_scopes;
79 };
80
81 /* optrecord_json_writer's ctor. Populate the top-level parts of the
82 in-memory JSON representation. */
83
84 optrecord_json_writer::optrecord_json_writer ()
85 : m_root_tuple (NULL), m_scopes ()
86 {
87 m_root_tuple = new json::array ();
88
89 /* Populate with metadata; compare with toplev.c: print_version. */
90 json::object *metadata = new json::object ();
91 m_root_tuple->append (metadata);
92 metadata->set ("format", new json::string ("1"));
93 json::object *generator = new json::object ();
94 metadata->set ("generator", generator);
95 generator->set ("name", new json::string (lang_hooks.name));
96 generator->set ("pkgversion", new json::string (pkgversion_string));
97 generator->set ("version", new json::string (version_string));
98 /* TARGET_NAME is passed in by the Makefile. */
99 generator->set ("target", new json::string (TARGET_NAME));
100
101 /* TODO: capture command-line?
102 see gen_producer_string in dwarf2out.c (currently static). */
103
104 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
105
106 json::array *passes = new json::array ();
107 m_root_tuple->append (passes);
108
109 /* Call add_pass_list for all of the pass lists. */
110 {
111 #define DEF_PASS_LIST(LIST) \
112 add_pass_list (passes, g->get_passes ()->LIST);
113 GCC_PASS_LISTS
114 #undef DEF_PASS_LIST
115 }
116
117 json::array *records = new json::array ();
118 m_root_tuple->append (records);
119
120 m_scopes.safe_push (records);
121 }
122
123 /* optrecord_json_writer's ctor.
124 Delete the in-memory JSON representation. */
125
126 optrecord_json_writer::~optrecord_json_writer ()
127 {
128 delete m_root_tuple;
129 }
130
131 /* Choose an appropriate filename, and write the saved records to it. */
132
133 void
134 optrecord_json_writer::write () const
135 {
136 char *filename = concat (dump_base_name, ".opt-record.json", NULL);
137 FILE *outfile = fopen (filename, "w");
138 if (outfile)
139 {
140 m_root_tuple->dump (outfile);
141 fclose (outfile);
142 }
143 else
144 error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
145 filename); // FIXME: more info?
146 free (filename);
147 }
148
149 /* Add a record for OPTINFO to the queue of records to be written. */
150
151 void
152 optrecord_json_writer::add_record (const optinfo *optinfo)
153 {
154 json::object *obj = optinfo_to_json (optinfo);
155
156 add_record (obj);
157
158 /* Potentially push the scope. */
159 if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
160 {
161 json::array *children = new json::array ();
162 obj->set ("children", children);
163 m_scopes.safe_push (children);
164 }
165 }
166
167 /* Private methods of optrecord_json_writer. */
168
169 /* Add record OBJ to the the innermost scope. */
170
171 void
172 optrecord_json_writer::add_record (json::object *obj)
173 {
174 /* Add to innermost scope. */
175 gcc_assert (m_scopes.length () > 0);
176 m_scopes[m_scopes.length () - 1]->append (obj);
177 }
178
179 /* Pop the innermost scope. */
180
181 void
182 optrecord_json_writer::pop_scope ()
183 {
184 m_scopes.pop ();
185 }
186
187 /* Create a JSON object representing LOC. */
188
189 json::object *
190 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
191 {
192 json::object *obj = new json::object ();
193 obj->set ("file", new json::string (loc.m_file));
194 obj->set ("line", new json::number (loc.m_line));
195 if (loc.m_function)
196 obj->set ("function", new json::string (loc.m_function));
197 return obj;
198 }
199
200 /* Create a JSON object representing LOC. */
201
202 json::object *
203 optrecord_json_writer::location_to_json (location_t loc)
204 {
205 json::object *obj = new json::object ();
206 obj->set ("file", new json::string (LOCATION_FILE (loc)));
207 obj->set ("line", new json::number (LOCATION_LINE (loc)));
208 obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
209 return obj;
210 }
211
212 /* Create a JSON object representing COUNT. */
213
214 json::object *
215 optrecord_json_writer::profile_count_to_json (profile_count count)
216 {
217 json::object *obj = new json::object ();
218 obj->set ("value", new json::number (count.to_gcov_type ()));
219 obj->set ("quality",
220 new json::string (profile_quality_as_string (count.quality ())));
221 return obj;
222 }
223
224 /* Get a string for use when referring to PASS in the saved optimization
225 records. */
226
227 json::string *
228 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
229 {
230 pretty_printer pp;
231 /* this is host-dependent, but will be consistent for a given host. */
232 pp_pointer (&pp, static_cast<void *> (pass));
233 return new json::string (pp_formatted_text (&pp));
234 }
235
236 /* Create a JSON object representing PASS. */
237
238 json::object *
239 optrecord_json_writer::pass_to_json (opt_pass *pass)
240 {
241 json::object *obj = new json::object ();
242 const char *type = NULL;
243 switch (pass->type)
244 {
245 default:
246 gcc_unreachable ();
247 case GIMPLE_PASS:
248 type = "gimple";
249 break;
250 case RTL_PASS:
251 type = "rtl";
252 break;
253 case SIMPLE_IPA_PASS:
254 type = "simple_ipa";
255 break;
256 case IPA_PASS:
257 type = "ipa";
258 break;
259 }
260 obj->set ("id", get_id_value_for_pass (pass));
261 obj->set ("type", new json::string (type));
262 obj->set ("name", new json::string (pass->name));
263 /* Represent the optgroup flags as an array. */
264 {
265 json::array *optgroups = new json::array ();
266 obj->set ("optgroups", optgroups);
267 for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
268 optgroup->name != NULL; optgroup++)
269 if (optgroup->value != OPTGROUP_ALL
270 && (pass->optinfo_flags & optgroup->value))
271 optgroups->append (new json::string (optgroup->name));
272 }
273 obj->set ("num", new json::number (pass->static_pass_number));
274 return obj;
275 }
276
277 /* Create a JSON array for LOC representing the chain of inlining
278 locations.
279 Compare with lhd_print_error_function and cp_print_error_function. */
280
281 json::value *
282 optrecord_json_writer::inlining_chain_to_json (location_t loc)
283 {
284 json::array *array = new json::array ();
285
286 tree abstract_origin = LOCATION_BLOCK (loc);
287
288 while (abstract_origin)
289 {
290 location_t *locus;
291 tree block = abstract_origin;
292
293 locus = &BLOCK_SOURCE_LOCATION (block);
294 tree fndecl = NULL;
295 block = BLOCK_SUPERCONTEXT (block);
296 while (block && TREE_CODE (block) == BLOCK
297 && BLOCK_ABSTRACT_ORIGIN (block))
298 {
299 tree ao = BLOCK_ABSTRACT_ORIGIN (block);
300
301 while (TREE_CODE (ao) == BLOCK
302 && BLOCK_ABSTRACT_ORIGIN (ao)
303 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
304 ao = BLOCK_ABSTRACT_ORIGIN (ao);
305
306 if (TREE_CODE (ao) == FUNCTION_DECL)
307 {
308 fndecl = ao;
309 break;
310 }
311 else if (TREE_CODE (ao) != BLOCK)
312 break;
313
314 block = BLOCK_SUPERCONTEXT (block);
315 }
316 if (fndecl)
317 abstract_origin = block;
318 else
319 {
320 while (block && TREE_CODE (block) == BLOCK)
321 block = BLOCK_SUPERCONTEXT (block);
322
323 if (block && TREE_CODE (block) == FUNCTION_DECL)
324 fndecl = block;
325 abstract_origin = NULL;
326 }
327 if (fndecl)
328 {
329 json::object *obj = new json::object ();
330 const char *printable_name
331 = lang_hooks.decl_printable_name (fndecl, 2);
332 obj->set ("fndecl", new json::string (printable_name));
333 if (*locus != UNKNOWN_LOCATION)
334 obj->set ("site", location_to_json (*locus));
335 array->append (obj);
336 }
337 }
338
339 return array;
340 }
341
342 /* Create a JSON object representing OPTINFO. */
343
344 json::object *
345 optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
346 {
347 json::object *obj = new json::object ();
348
349 obj->set ("impl_location",
350 impl_location_to_json (optinfo->get_impl_location ()));
351
352 const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
353 obj->set ("kind", new json::string (kind_str));
354 json::array *message = new json::array ();
355 obj->set ("message", message);
356 for (unsigned i = 0; i < optinfo->num_items (); i++)
357 {
358 const optinfo_item *item = optinfo->get_item (i);
359 switch (item->get_kind ())
360 {
361 default:
362 gcc_unreachable ();
363 case OPTINFO_ITEM_KIND_TEXT:
364 {
365 message->append (new json::string (item->get_text ()));
366 }
367 break;
368 case OPTINFO_ITEM_KIND_TREE:
369 {
370 json::object *json_item = new json::object ();
371 json_item->set ("expr", new json::string (item->get_text ()));
372
373 /* Capture any location for the node. */
374 if (item->get_location () != UNKNOWN_LOCATION)
375 json_item->set ("location", location_to_json (item->get_location ()));
376
377 message->append (json_item);
378 }
379 break;
380 case OPTINFO_ITEM_KIND_GIMPLE:
381 {
382 json::object *json_item = new json::object ();
383 json_item->set ("stmt", new json::string (item->get_text ()));
384
385 /* Capture any location for the stmt. */
386 if (item->get_location () != UNKNOWN_LOCATION)
387 json_item->set ("location", location_to_json (item->get_location ()));
388
389 message->append (json_item);
390 }
391 break;
392 case OPTINFO_ITEM_KIND_SYMTAB_NODE:
393 {
394 json::object *json_item = new json::object ();
395 json_item->set ("symtab_node", new json::string (item->get_text ()));
396
397 /* Capture any location for the node. */
398 if (item->get_location () != UNKNOWN_LOCATION)
399 json_item->set ("location", location_to_json (item->get_location ()));
400 message->append (json_item);
401 }
402 break;
403 }
404 }
405
406 if (optinfo->get_pass ())
407 obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
408
409 profile_count count = optinfo->get_count ();
410 if (count.initialized_p ())
411 obj->set ("count", profile_count_to_json (count));
412
413 /* Record any location, handling the case where of an UNKNOWN_LOCATION
414 within an inlined block. */
415 location_t loc = optinfo->get_location_t ();
416 if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
417 {
418 // TOOD: record the location (just caret for now)
419 // TODO: start/finish also?
420 obj->set ("location", location_to_json (loc));
421 }
422
423 if (current_function_decl)
424 {
425 const char *fnname = get_fnname_from_decl (current_function_decl);
426 obj->set ("function", new json::string (fnname));
427 }
428
429 if (loc != UNKNOWN_LOCATION)
430 obj->set ("inlining_chain", inlining_chain_to_json (loc));
431
432 return obj;
433 }
434
435 /* Add a json description of PASS and its siblings to ARR, recursing into
436 child passes (adding their descriptions within a "children" array). */
437
438 void
439 optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
440 {
441 do
442 {
443 json::object *pass_obj = pass_to_json (pass);
444 arr->append (pass_obj);
445 if (pass->sub)
446 {
447 json::array *sub = new json::array ();
448 pass_obj->set ("children", sub);
449 add_pass_list (sub, pass->sub);
450 }
451 pass = pass->next;
452 }
453 while (pass);
454 }
455
456 /* File-level interface to rest of compiler (to avoid exposing
457 class optrecord_json_writer outside of this file). */
458
459 static optrecord_json_writer *the_json_writer;
460
461 /* Perform startup activity for -fsave-optimization-record. */
462
463 void
464 optimization_records_start ()
465 {
466 /* Bail if recording not enabled. */
467 if (!flag_save_optimization_record)
468 return;
469
470 the_json_writer = new optrecord_json_writer ();
471 }
472
473 /* Perform cleanup activity for -fsave-optimization-record.
474
475 Currently, the file is written out here in one go, before cleaning
476 up. */
477
478 void
479 optimization_records_finish ()
480 {
481 /* Bail if recording not enabled. */
482 if (!the_json_writer)
483 return;
484
485 the_json_writer->write ();
486
487 delete the_json_writer;
488 the_json_writer = NULL;
489 }
490
491 /* Did the user request optimization records to be written out? */
492
493 bool
494 optimization_records_enabled_p ()
495 {
496 return the_json_writer != NULL;
497 }
498
499 /* If optimization records were requested, then add a record for OPTINFO
500 to the queue of records to be written. */
501
502 void
503 optimization_records_maybe_record_optinfo (const optinfo *optinfo)
504 {
505 /* Bail if recording not enabled. */
506 if (!the_json_writer)
507 return;
508
509 the_json_writer->add_record (optinfo);
510 }
511
512 /* Handling for the end of a dump scope for the
513 optimization records sink. */
514
515 void
516 optimization_records_maybe_pop_dump_scope ()
517 {
518 /* Bail if recording not enabled. */
519 if (!the_json_writer)
520 return;
521
522 the_json_writer->pop_scope ();
523 }
524
525 #if CHECKING_P
526
527 namespace selftest {
528
529 /* Verify that we can build a JSON optimization record from dump_*
530 calls. */
531
532 static void
533 test_building_json_from_dump_calls ()
534 {
535 temp_dump_context tmp (true);
536 dump_location_t loc;
537 dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
538 dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
539 optinfo *info = tmp.get_pending_optinfo ();
540 ASSERT_TRUE (info != NULL);
541 ASSERT_EQ (info->num_items (), 2);
542
543 optrecord_json_writer writer;
544 json::object *json_obj = writer.optinfo_to_json (info);
545 ASSERT_TRUE (json_obj != NULL);
546
547 /* Verify that the json is sane. */
548 pretty_printer pp;
549 json_obj->print (&pp);
550 const char *json_str = pp_formatted_text (&pp);
551 ASSERT_STR_CONTAINS (json_str, "impl_location");
552 ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
553 ASSERT_STR_CONTAINS (json_str,
554 "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
555 delete json_obj;
556 }
557
558 /* Run all of the selftests within this file. */
559
560 void
561 optinfo_emit_json_cc_tests ()
562 {
563 test_building_json_from_dump_calls ();
564 }
565
566 } // namespace selftest
567
568 #endif /* CHECKING_P */