Remove nfs bogon
[official-gcc.git] / gcc / optinfo-emit-json.cc
blob992960ec8d200a5b2cccd2edab46e89ec40e3cf4
1 /* Emit optimization information as JSON files.
2 Copyright (C) 2018 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
25 #include "backend.h"
26 #include "tree.h"
27 #include "gimple.h"
28 #include "diagnostic-core.h"
30 #include "profile.h"
31 #include "output.h"
32 #include "tree-pass.h"
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"
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"
49 /* A class for writing out optimization records in JSON format. */
51 class optrecord_json_writer
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 ();
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);
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;
77 /* The currently open scopes, for expressing nested optimization records. */
78 auto_vec<json::array *> m_scopes;
81 /* optrecord_json_writer's ctor. Populate the top-level parts of the
82 in-memory JSON representation. */
84 optrecord_json_writer::optrecord_json_writer ()
85 : m_root_tuple (NULL), m_scopes ()
87 m_root_tuple = new json::array ();
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));
101 /* TODO: capture command-line?
102 see gen_producer_string in dwarf2out.c (currently static). */
104 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
106 json::array *passes = new json::array ();
107 m_root_tuple->append (passes);
109 /* Call add_pass_list for all of the pass lists. */
111 #define DEF_PASS_LIST(LIST) \
112 add_pass_list (passes, g->get_passes ()->LIST);
113 GCC_PASS_LISTS
114 #undef DEF_PASS_LIST
117 json::array *records = new json::array ();
118 m_root_tuple->append (records);
120 m_scopes.safe_push (records);
123 /* optrecord_json_writer's ctor.
124 Delete the in-memory JSON representation. */
126 optrecord_json_writer::~optrecord_json_writer ()
128 delete m_root_tuple;
131 /* Choose an appropriate filename, and write the saved records to it. */
133 void
134 optrecord_json_writer::write () const
136 char *filename = concat (dump_base_name, ".opt-record.json", NULL);
137 FILE *outfile = fopen (filename, "w");
138 if (outfile)
140 m_root_tuple->dump (outfile);
141 fclose (outfile);
143 else
144 error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
145 filename); // FIXME: more info?
146 free (filename);
149 /* Add a record for OPTINFO to the queue of records to be written. */
151 void
152 optrecord_json_writer::add_record (const optinfo *optinfo)
154 json::object *obj = optinfo_to_json (optinfo);
156 add_record (obj);
158 /* Potentially push the scope. */
159 if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
161 json::array *children = new json::array ();
162 obj->set ("children", children);
163 m_scopes.safe_push (children);
167 /* Private methods of optrecord_json_writer. */
169 /* Add record OBJ to the the innermost scope. */
171 void
172 optrecord_json_writer::add_record (json::object *obj)
174 /* Add to innermost scope. */
175 gcc_assert (m_scopes.length () > 0);
176 m_scopes[m_scopes.length () - 1]->append (obj);
179 /* Pop the innermost scope. */
181 void
182 optrecord_json_writer::pop_scope ()
184 m_scopes.pop ();
187 /* Create a JSON object representing LOC. */
189 json::object *
190 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
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;
200 /* Create a JSON object representing LOC. */
202 json::object *
203 optrecord_json_writer::location_to_json (location_t loc)
205 gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
206 expanded_location exploc = expand_location (loc);
207 json::object *obj = new json::object ();
208 obj->set ("file", new json::string (exploc.file));
209 obj->set ("line", new json::number (exploc.line));
210 obj->set ("column", new json::number (exploc.column));
211 return obj;
214 /* Create a JSON object representing COUNT. */
216 json::object *
217 optrecord_json_writer::profile_count_to_json (profile_count count)
219 json::object *obj = new json::object ();
220 obj->set ("value", new json::number (count.to_gcov_type ()));
221 obj->set ("quality",
222 new json::string (profile_quality_as_string (count.quality ())));
223 return obj;
226 /* Get a string for use when referring to PASS in the saved optimization
227 records. */
229 json::string *
230 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
232 pretty_printer pp;
233 /* this is host-dependent, but will be consistent for a given host. */
234 pp_pointer (&pp, static_cast<void *> (pass));
235 return new json::string (pp_formatted_text (&pp));
238 /* Create a JSON object representing PASS. */
240 json::object *
241 optrecord_json_writer::pass_to_json (opt_pass *pass)
243 json::object *obj = new json::object ();
244 const char *type = NULL;
245 switch (pass->type)
247 default:
248 gcc_unreachable ();
249 case GIMPLE_PASS:
250 type = "gimple";
251 break;
252 case RTL_PASS:
253 type = "rtl";
254 break;
255 case SIMPLE_IPA_PASS:
256 type = "simple_ipa";
257 break;
258 case IPA_PASS:
259 type = "ipa";
260 break;
262 obj->set ("id", get_id_value_for_pass (pass));
263 obj->set ("type", new json::string (type));
264 obj->set ("name", new json::string (pass->name));
265 /* Represent the optgroup flags as an array. */
267 json::array *optgroups = new json::array ();
268 obj->set ("optgroups", optgroups);
269 for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
270 optgroup->name != NULL; optgroup++)
271 if (optgroup->value != OPTGROUP_ALL
272 && (pass->optinfo_flags & optgroup->value))
273 optgroups->append (new json::string (optgroup->name));
275 obj->set ("num", new json::number (pass->static_pass_number));
276 return obj;
279 /* Create a JSON array for LOC representing the chain of inlining
280 locations.
281 Compare with lhd_print_error_function and cp_print_error_function. */
283 json::value *
284 optrecord_json_writer::inlining_chain_to_json (location_t loc)
286 json::array *array = new json::array ();
288 tree abstract_origin = LOCATION_BLOCK (loc);
290 while (abstract_origin)
292 location_t *locus;
293 tree block = abstract_origin;
295 locus = &BLOCK_SOURCE_LOCATION (block);
296 tree fndecl = NULL;
297 block = BLOCK_SUPERCONTEXT (block);
298 while (block && TREE_CODE (block) == BLOCK
299 && BLOCK_ABSTRACT_ORIGIN (block))
301 tree ao = BLOCK_ABSTRACT_ORIGIN (block);
303 while (TREE_CODE (ao) == BLOCK
304 && BLOCK_ABSTRACT_ORIGIN (ao)
305 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
306 ao = BLOCK_ABSTRACT_ORIGIN (ao);
308 if (TREE_CODE (ao) == FUNCTION_DECL)
310 fndecl = ao;
311 break;
313 else if (TREE_CODE (ao) != BLOCK)
314 break;
316 block = BLOCK_SUPERCONTEXT (block);
318 if (fndecl)
319 abstract_origin = block;
320 else
322 while (block && TREE_CODE (block) == BLOCK)
323 block = BLOCK_SUPERCONTEXT (block);
325 if (block && TREE_CODE (block) == FUNCTION_DECL)
326 fndecl = block;
327 abstract_origin = NULL;
329 if (fndecl)
331 json::object *obj = new json::object ();
332 const char *printable_name
333 = lang_hooks.decl_printable_name (fndecl, 2);
334 obj->set ("fndecl", new json::string (printable_name));
335 if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
336 obj->set ("site", location_to_json (*locus));
337 array->append (obj);
341 return array;
344 /* Create a JSON object representing OPTINFO. */
346 json::object *
347 optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
349 json::object *obj = new json::object ();
351 obj->set ("impl_location",
352 impl_location_to_json (optinfo->get_impl_location ()));
354 const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
355 obj->set ("kind", new json::string (kind_str));
356 json::array *message = new json::array ();
357 obj->set ("message", message);
358 for (unsigned i = 0; i < optinfo->num_items (); i++)
360 const optinfo_item *item = optinfo->get_item (i);
361 switch (item->get_kind ())
363 default:
364 gcc_unreachable ();
365 case OPTINFO_ITEM_KIND_TEXT:
367 message->append (new json::string (item->get_text ()));
369 break;
370 case OPTINFO_ITEM_KIND_TREE:
372 json::object *json_item = new json::object ();
373 json_item->set ("expr", new json::string (item->get_text ()));
375 /* Capture any location for the node. */
376 if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
377 json_item->set ("location",
378 location_to_json (item->get_location ()));
380 message->append (json_item);
382 break;
383 case OPTINFO_ITEM_KIND_GIMPLE:
385 json::object *json_item = new json::object ();
386 json_item->set ("stmt", new json::string (item->get_text ()));
388 /* Capture any location for the stmt. */
389 if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
390 json_item->set ("location",
391 location_to_json (item->get_location ()));
393 message->append (json_item);
395 break;
396 case OPTINFO_ITEM_KIND_SYMTAB_NODE:
398 json::object *json_item = new json::object ();
399 json_item->set ("symtab_node", new json::string (item->get_text ()));
401 /* Capture any location for the node. */
402 if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
403 json_item->set ("location",
404 location_to_json (item->get_location ()));
405 message->append (json_item);
407 break;
411 if (optinfo->get_pass ())
412 obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
414 profile_count count = optinfo->get_count ();
415 if (count.initialized_p ())
416 obj->set ("count", profile_count_to_json (count));
418 /* Record any location, handling the case where of an UNKNOWN_LOCATION
419 within an inlined block. */
420 location_t loc = optinfo->get_location_t ();
421 if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
423 // TOOD: record the location (just caret for now)
424 // TODO: start/finish also?
425 obj->set ("location", location_to_json (loc));
428 if (current_function_decl)
430 const char *fnname = get_fnname_from_decl (current_function_decl);
431 obj->set ("function", new json::string (fnname));
434 if (loc != UNKNOWN_LOCATION)
435 obj->set ("inlining_chain", inlining_chain_to_json (loc));
437 return obj;
440 /* Add a json description of PASS and its siblings to ARR, recursing into
441 child passes (adding their descriptions within a "children" array). */
443 void
444 optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
448 json::object *pass_obj = pass_to_json (pass);
449 arr->append (pass_obj);
450 if (pass->sub)
452 json::array *sub = new json::array ();
453 pass_obj->set ("children", sub);
454 add_pass_list (sub, pass->sub);
456 pass = pass->next;
458 while (pass);
461 /* File-level interface to rest of compiler (to avoid exposing
462 class optrecord_json_writer outside of this file). */
464 static optrecord_json_writer *the_json_writer;
466 /* Perform startup activity for -fsave-optimization-record. */
468 void
469 optimization_records_start ()
471 /* Bail if recording not enabled. */
472 if (!flag_save_optimization_record)
473 return;
475 the_json_writer = new optrecord_json_writer ();
478 /* Perform cleanup activity for -fsave-optimization-record.
480 Currently, the file is written out here in one go, before cleaning
481 up. */
483 void
484 optimization_records_finish ()
486 /* Bail if recording not enabled. */
487 if (!the_json_writer)
488 return;
490 the_json_writer->write ();
492 delete the_json_writer;
493 the_json_writer = NULL;
496 /* Did the user request optimization records to be written out? */
498 bool
499 optimization_records_enabled_p ()
501 return the_json_writer != NULL;
504 /* If optimization records were requested, then add a record for OPTINFO
505 to the queue of records to be written. */
507 void
508 optimization_records_maybe_record_optinfo (const optinfo *optinfo)
510 /* Bail if recording not enabled. */
511 if (!the_json_writer)
512 return;
514 the_json_writer->add_record (optinfo);
517 /* Handling for the end of a dump scope for the
518 optimization records sink. */
520 void
521 optimization_records_maybe_pop_dump_scope ()
523 /* Bail if recording not enabled. */
524 if (!the_json_writer)
525 return;
527 the_json_writer->pop_scope ();
530 #if CHECKING_P
532 namespace selftest {
534 /* Verify that we can build a JSON optimization record from dump_*
535 calls. */
537 static void
538 test_building_json_from_dump_calls ()
540 temp_dump_context tmp (true, MSG_NOTE);
541 dump_location_t loc;
542 dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
543 dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
544 optinfo *info = tmp.get_pending_optinfo ();
545 ASSERT_TRUE (info != NULL);
546 ASSERT_EQ (info->num_items (), 2);
548 optrecord_json_writer writer;
549 json::object *json_obj = writer.optinfo_to_json (info);
550 ASSERT_TRUE (json_obj != NULL);
552 /* Verify that the json is sane. */
553 pretty_printer pp;
554 json_obj->print (&pp);
555 const char *json_str = pp_formatted_text (&pp);
556 ASSERT_STR_CONTAINS (json_str, "impl_location");
557 ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
558 ASSERT_STR_CONTAINS (json_str,
559 "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
560 delete json_obj;
563 /* Run all of the selftests within this file. */
565 void
566 optinfo_emit_json_cc_tests ()
568 test_building_json_from_dump_calls ();
571 } // namespace selftest
573 #endif /* CHECKING_P */