Allow target to override gnu-user.h crti and crtn
[official-gcc.git] / gcc / optinfo-emit-json.cc
blob6d4502c512940a015f610376461099545318a2ef
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"
48 #include <zlib.h>
50 /* A class for writing out optimization records in JSON format. */
52 class optrecord_json_writer
54 public:
55 optrecord_json_writer ();
56 ~optrecord_json_writer ();
57 void write () const;
58 void add_record (const optinfo *optinfo);
59 void pop_scope ();
61 void add_record (json::object *obj);
62 json::object *impl_location_to_json (dump_impl_location_t loc);
63 json::object *location_to_json (location_t loc);
64 json::object *profile_count_to_json (profile_count count);
65 json::string *get_id_value_for_pass (opt_pass *pass);
66 json::object *pass_to_json (opt_pass *pass);
67 json::value *inlining_chain_to_json (location_t loc);
68 json::object *optinfo_to_json (const optinfo *optinfo);
69 void add_pass_list (json::array *arr, opt_pass *pass);
71 private:
72 /* The root value for the JSON file.
73 Currently the JSON values are stored in memory, and flushed when the
74 compiler exits. It would probably be better to simply write out
75 the JSON as we go. */
76 json::array *m_root_tuple;
78 /* The currently open scopes, for expressing nested optimization records. */
79 auto_vec<json::array *> m_scopes;
82 /* optrecord_json_writer's ctor. Populate the top-level parts of the
83 in-memory JSON representation. */
85 optrecord_json_writer::optrecord_json_writer ()
86 : m_root_tuple (NULL), m_scopes ()
88 m_root_tuple = new json::array ();
90 /* Populate with metadata; compare with toplev.c: print_version. */
91 json::object *metadata = new json::object ();
92 m_root_tuple->append (metadata);
93 metadata->set ("format", new json::string ("1"));
94 json::object *generator = new json::object ();
95 metadata->set ("generator", generator);
96 generator->set ("name", new json::string (lang_hooks.name));
97 generator->set ("pkgversion", new json::string (pkgversion_string));
98 generator->set ("version", new json::string (version_string));
99 /* TARGET_NAME is passed in by the Makefile. */
100 generator->set ("target", new json::string (TARGET_NAME));
102 /* TODO: capture command-line?
103 see gen_producer_string in dwarf2out.c (currently static). */
105 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
107 json::array *passes = new json::array ();
108 m_root_tuple->append (passes);
110 /* Call add_pass_list for all of the pass lists. */
112 #define DEF_PASS_LIST(LIST) \
113 add_pass_list (passes, g->get_passes ()->LIST);
114 GCC_PASS_LISTS
115 #undef DEF_PASS_LIST
118 json::array *records = new json::array ();
119 m_root_tuple->append (records);
121 m_scopes.safe_push (records);
124 /* optrecord_json_writer's ctor.
125 Delete the in-memory JSON representation. */
127 optrecord_json_writer::~optrecord_json_writer ()
129 delete m_root_tuple;
132 /* Choose an appropriate filename, and write the saved records to it. */
134 void
135 optrecord_json_writer::write () const
137 pretty_printer pp;
138 m_root_tuple->print (&pp);
140 bool emitted_error = false;
141 char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
142 gzFile outfile = gzopen (filename, "w");
143 if (outfile == NULL)
145 error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
146 filename); // FIXME: more info?
147 goto cleanup;
150 if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
152 int tmp;
153 error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
154 filename, gzerror (outfile, &tmp));
155 emitted_error = true;
158 cleanup:
159 if (outfile)
160 if (gzclose (outfile) != Z_OK)
161 if (!emitted_error)
162 error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
163 filename);
165 free (filename);
168 /* Add a record for OPTINFO to the queue of records to be written. */
170 void
171 optrecord_json_writer::add_record (const optinfo *optinfo)
173 json::object *obj = optinfo_to_json (optinfo);
175 add_record (obj);
177 /* Potentially push the scope. */
178 if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
180 json::array *children = new json::array ();
181 obj->set ("children", children);
182 m_scopes.safe_push (children);
186 /* Private methods of optrecord_json_writer. */
188 /* Add record OBJ to the the innermost scope. */
190 void
191 optrecord_json_writer::add_record (json::object *obj)
193 /* Add to innermost scope. */
194 gcc_assert (m_scopes.length () > 0);
195 m_scopes[m_scopes.length () - 1]->append (obj);
198 /* Pop the innermost scope. */
200 void
201 optrecord_json_writer::pop_scope ()
203 m_scopes.pop ();
206 /* Create a JSON object representing LOC. */
208 json::object *
209 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
211 json::object *obj = new json::object ();
212 obj->set ("file", new json::string (loc.m_file));
213 obj->set ("line", new json::number (loc.m_line));
214 if (loc.m_function)
215 obj->set ("function", new json::string (loc.m_function));
216 return obj;
219 /* Create a JSON object representing LOC. */
221 json::object *
222 optrecord_json_writer::location_to_json (location_t loc)
224 gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
225 expanded_location exploc = expand_location (loc);
226 json::object *obj = new json::object ();
227 obj->set ("file", new json::string (exploc.file));
228 obj->set ("line", new json::number (exploc.line));
229 obj->set ("column", new json::number (exploc.column));
230 return obj;
233 /* Create a JSON object representing COUNT. */
235 json::object *
236 optrecord_json_writer::profile_count_to_json (profile_count count)
238 json::object *obj = new json::object ();
239 obj->set ("value", new json::number (count.to_gcov_type ()));
240 obj->set ("quality",
241 new json::string (profile_quality_as_string (count.quality ())));
242 return obj;
245 /* Get a string for use when referring to PASS in the saved optimization
246 records. */
248 json::string *
249 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
251 pretty_printer pp;
252 /* this is host-dependent, but will be consistent for a given host. */
253 pp_pointer (&pp, static_cast<void *> (pass));
254 return new json::string (pp_formatted_text (&pp));
257 /* Create a JSON object representing PASS. */
259 json::object *
260 optrecord_json_writer::pass_to_json (opt_pass *pass)
262 json::object *obj = new json::object ();
263 const char *type = NULL;
264 switch (pass->type)
266 default:
267 gcc_unreachable ();
268 case GIMPLE_PASS:
269 type = "gimple";
270 break;
271 case RTL_PASS:
272 type = "rtl";
273 break;
274 case SIMPLE_IPA_PASS:
275 type = "simple_ipa";
276 break;
277 case IPA_PASS:
278 type = "ipa";
279 break;
281 obj->set ("id", get_id_value_for_pass (pass));
282 obj->set ("type", new json::string (type));
283 obj->set ("name", new json::string (pass->name));
284 /* Represent the optgroup flags as an array. */
286 json::array *optgroups = new json::array ();
287 obj->set ("optgroups", optgroups);
288 for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
289 optgroup->name != NULL; optgroup++)
290 if (optgroup->value != OPTGROUP_ALL
291 && (pass->optinfo_flags & optgroup->value))
292 optgroups->append (new json::string (optgroup->name));
294 obj->set ("num", new json::number (pass->static_pass_number));
295 return obj;
298 /* Create a JSON array for LOC representing the chain of inlining
299 locations.
300 Compare with lhd_print_error_function and cp_print_error_function. */
302 json::value *
303 optrecord_json_writer::inlining_chain_to_json (location_t loc)
305 json::array *array = new json::array ();
307 tree abstract_origin = LOCATION_BLOCK (loc);
309 while (abstract_origin)
311 location_t *locus;
312 tree block = abstract_origin;
314 locus = &BLOCK_SOURCE_LOCATION (block);
315 tree fndecl = NULL;
316 block = BLOCK_SUPERCONTEXT (block);
317 while (block && TREE_CODE (block) == BLOCK
318 && BLOCK_ABSTRACT_ORIGIN (block))
320 tree ao = BLOCK_ABSTRACT_ORIGIN (block);
321 if (TREE_CODE (ao) == FUNCTION_DECL)
323 fndecl = ao;
324 break;
326 else if (TREE_CODE (ao) != BLOCK)
327 break;
329 block = BLOCK_SUPERCONTEXT (block);
331 if (fndecl)
332 abstract_origin = block;
333 else
335 while (block && TREE_CODE (block) == BLOCK)
336 block = BLOCK_SUPERCONTEXT (block);
338 if (block && TREE_CODE (block) == FUNCTION_DECL)
339 fndecl = block;
340 abstract_origin = NULL;
342 if (fndecl)
344 json::object *obj = new json::object ();
345 const char *printable_name
346 = lang_hooks.decl_printable_name (fndecl, 2);
347 obj->set ("fndecl", new json::string (printable_name));
348 if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
349 obj->set ("site", location_to_json (*locus));
350 array->append (obj);
354 return array;
357 /* Create a JSON object representing OPTINFO. */
359 json::object *
360 optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
362 json::object *obj = new json::object ();
364 obj->set ("impl_location",
365 impl_location_to_json (optinfo->get_impl_location ()));
367 const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
368 obj->set ("kind", new json::string (kind_str));
369 json::array *message = new json::array ();
370 obj->set ("message", message);
371 for (unsigned i = 0; i < optinfo->num_items (); i++)
373 const optinfo_item *item = optinfo->get_item (i);
374 switch (item->get_kind ())
376 default:
377 gcc_unreachable ();
378 case OPTINFO_ITEM_KIND_TEXT:
380 message->append (new json::string (item->get_text ()));
382 break;
383 case OPTINFO_ITEM_KIND_TREE:
385 json::object *json_item = new json::object ();
386 json_item->set ("expr", new json::string (item->get_text ()));
388 /* Capture any location for the node. */
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_GIMPLE:
398 json::object *json_item = new json::object ();
399 json_item->set ("stmt", new json::string (item->get_text ()));
401 /* Capture any location for the stmt. */
402 if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
403 json_item->set ("location",
404 location_to_json (item->get_location ()));
406 message->append (json_item);
408 break;
409 case OPTINFO_ITEM_KIND_SYMTAB_NODE:
411 json::object *json_item = new json::object ();
412 json_item->set ("symtab_node", new json::string (item->get_text ()));
414 /* Capture any location for the node. */
415 if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
416 json_item->set ("location",
417 location_to_json (item->get_location ()));
418 message->append (json_item);
420 break;
424 if (optinfo->get_pass ())
425 obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
427 profile_count count = optinfo->get_count ();
428 if (count.initialized_p ())
429 obj->set ("count", profile_count_to_json (count));
431 /* Record any location, handling the case where of an UNKNOWN_LOCATION
432 within an inlined block. */
433 location_t loc = optinfo->get_location_t ();
434 if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
436 // TOOD: record the location (just caret for now)
437 // TODO: start/finish also?
438 obj->set ("location", location_to_json (loc));
441 if (current_function_decl)
443 const char *fnname = get_fnname_from_decl (current_function_decl);
444 obj->set ("function", new json::string (fnname));
447 if (loc != UNKNOWN_LOCATION)
448 obj->set ("inlining_chain", inlining_chain_to_json (loc));
450 return obj;
453 /* Add a json description of PASS and its siblings to ARR, recursing into
454 child passes (adding their descriptions within a "children" array). */
456 void
457 optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
461 json::object *pass_obj = pass_to_json (pass);
462 arr->append (pass_obj);
463 if (pass->sub)
465 json::array *sub = new json::array ();
466 pass_obj->set ("children", sub);
467 add_pass_list (sub, pass->sub);
469 pass = pass->next;
471 while (pass);
474 /* File-level interface to rest of compiler (to avoid exposing
475 class optrecord_json_writer outside of this file). */
477 static optrecord_json_writer *the_json_writer;
479 /* Perform startup activity for -fsave-optimization-record. */
481 void
482 optimization_records_start ()
484 /* Bail if recording not enabled. */
485 if (!flag_save_optimization_record)
486 return;
488 the_json_writer = new optrecord_json_writer ();
491 /* Perform cleanup activity for -fsave-optimization-record.
493 Currently, the file is written out here in one go, before cleaning
494 up. */
496 void
497 optimization_records_finish ()
499 /* Bail if recording not enabled. */
500 if (!the_json_writer)
501 return;
503 the_json_writer->write ();
505 delete the_json_writer;
506 the_json_writer = NULL;
509 /* Did the user request optimization records to be written out? */
511 bool
512 optimization_records_enabled_p ()
514 return the_json_writer != NULL;
517 /* If optimization records were requested, then add a record for OPTINFO
518 to the queue of records to be written. */
520 void
521 optimization_records_maybe_record_optinfo (const optinfo *optinfo)
523 /* Bail if recording not enabled. */
524 if (!the_json_writer)
525 return;
527 the_json_writer->add_record (optinfo);
530 /* Handling for the end of a dump scope for the
531 optimization records sink. */
533 void
534 optimization_records_maybe_pop_dump_scope ()
536 /* Bail if recording not enabled. */
537 if (!the_json_writer)
538 return;
540 the_json_writer->pop_scope ();
543 #if CHECKING_P
545 namespace selftest {
547 /* Verify that we can build a JSON optimization record from dump_*
548 calls. */
550 static void
551 test_building_json_from_dump_calls ()
553 temp_dump_context tmp (true, true, MSG_NOTE);
554 dump_location_t loc;
555 dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
556 dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
557 optinfo *info = tmp.get_pending_optinfo ();
558 ASSERT_TRUE (info != NULL);
559 ASSERT_EQ (info->num_items (), 2);
561 optrecord_json_writer writer;
562 json::object *json_obj = writer.optinfo_to_json (info);
563 ASSERT_TRUE (json_obj != NULL);
565 /* Verify that the json is sane. */
566 pretty_printer pp;
567 json_obj->print (&pp);
568 const char *json_str = pp_formatted_text (&pp);
569 ASSERT_STR_CONTAINS (json_str, "impl_location");
570 ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
571 ASSERT_STR_CONTAINS (json_str,
572 "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
573 delete json_obj;
576 /* Run all of the selftests within this file. */
578 void
579 optinfo_emit_json_cc_tests ()
581 test_building_json_from_dump_calls ();
584 } // namespace selftest
586 #endif /* CHECKING_P */