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
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
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/>. */
23 #include "coretypes.h"
28 #include "diagnostic-core.h"
32 #include "tree-pass.h"
35 #include "optinfo-emit-json.h"
37 #include "pretty-print.h"
38 #include "tree-pretty-print.h"
39 #include "gimple-pretty-print.h"
42 #include "langhooks.h"
45 #include "pass_manager.h"
47 #include "dump-context.h"
49 /* A class for writing out optimization records in JSON format. */
51 class optrecord_json_writer
54 optrecord_json_writer ();
55 ~optrecord_json_writer ();
57 void add_record (const optinfo
*optinfo
);
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
);
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
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);
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 ()
131 /* Choose an appropriate filename, and write the saved records to it. */
134 optrecord_json_writer::write () const
136 char *filename
= concat (dump_base_name
, ".opt-record.json", NULL
);
137 FILE *outfile
= fopen (filename
, "w");
140 m_root_tuple
->dump (outfile
);
144 error_at (UNKNOWN_LOCATION
, "unable to write optimization records to %qs",
145 filename
); // FIXME: more info?
149 /* Add a record for OPTINFO to the queue of records to be written. */
152 optrecord_json_writer::add_record (const optinfo
*optinfo
)
154 json::object
*obj
= optinfo_to_json (optinfo
);
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. */
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. */
182 optrecord_json_writer::pop_scope ()
187 /* Create a JSON object representing LOC. */
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
));
196 obj
->set ("function", new json::string (loc
.m_function
));
200 /* Create a JSON object representing LOC. */
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
));
214 /* Create a JSON object representing COUNT. */
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 ()));
222 new json::string (profile_quality_as_string (count
.quality ())));
226 /* Get a string for use when referring to PASS in the saved optimization
230 optrecord_json_writer::get_id_value_for_pass (opt_pass
*pass
)
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. */
241 optrecord_json_writer::pass_to_json (opt_pass
*pass
)
243 json::object
*obj
= new json::object ();
244 const char *type
= NULL
;
255 case SIMPLE_IPA_PASS
:
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
));
279 /* Create a JSON array for LOC representing the chain of inlining
281 Compare with lhd_print_error_function and cp_print_error_function. */
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
)
293 tree block
= abstract_origin
;
295 locus
= &BLOCK_SOURCE_LOCATION (block
);
297 block
= BLOCK_SUPERCONTEXT (block
);
298 while (block
&& TREE_CODE (block
) == BLOCK
299 && BLOCK_ABSTRACT_ORIGIN (block
))
301 tree ao
= BLOCK_ABSTRACT_ORIGIN (block
);
302 if (TREE_CODE (ao
) == FUNCTION_DECL
)
307 else if (TREE_CODE (ao
) != BLOCK
)
310 block
= BLOCK_SUPERCONTEXT (block
);
313 abstract_origin
= block
;
316 while (block
&& TREE_CODE (block
) == BLOCK
)
317 block
= BLOCK_SUPERCONTEXT (block
);
319 if (block
&& TREE_CODE (block
) == FUNCTION_DECL
)
321 abstract_origin
= NULL
;
325 json::object
*obj
= new json::object ();
326 const char *printable_name
327 = lang_hooks
.decl_printable_name (fndecl
, 2);
328 obj
->set ("fndecl", new json::string (printable_name
));
329 if (LOCATION_LOCUS (*locus
) != UNKNOWN_LOCATION
)
330 obj
->set ("site", location_to_json (*locus
));
338 /* Create a JSON object representing OPTINFO. */
341 optrecord_json_writer::optinfo_to_json (const optinfo
*optinfo
)
343 json::object
*obj
= new json::object ();
345 obj
->set ("impl_location",
346 impl_location_to_json (optinfo
->get_impl_location ()));
348 const char *kind_str
= optinfo_kind_to_string (optinfo
->get_kind ());
349 obj
->set ("kind", new json::string (kind_str
));
350 json::array
*message
= new json::array ();
351 obj
->set ("message", message
);
352 for (unsigned i
= 0; i
< optinfo
->num_items (); i
++)
354 const optinfo_item
*item
= optinfo
->get_item (i
);
355 switch (item
->get_kind ())
359 case OPTINFO_ITEM_KIND_TEXT
:
361 message
->append (new json::string (item
->get_text ()));
364 case OPTINFO_ITEM_KIND_TREE
:
366 json::object
*json_item
= new json::object ();
367 json_item
->set ("expr", new json::string (item
->get_text ()));
369 /* Capture any location for the node. */
370 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
371 json_item
->set ("location",
372 location_to_json (item
->get_location ()));
374 message
->append (json_item
);
377 case OPTINFO_ITEM_KIND_GIMPLE
:
379 json::object
*json_item
= new json::object ();
380 json_item
->set ("stmt", new json::string (item
->get_text ()));
382 /* Capture any location for the stmt. */
383 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
384 json_item
->set ("location",
385 location_to_json (item
->get_location ()));
387 message
->append (json_item
);
390 case OPTINFO_ITEM_KIND_SYMTAB_NODE
:
392 json::object
*json_item
= new json::object ();
393 json_item
->set ("symtab_node", new json::string (item
->get_text ()));
395 /* Capture any location for the node. */
396 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
397 json_item
->set ("location",
398 location_to_json (item
->get_location ()));
399 message
->append (json_item
);
405 if (optinfo
->get_pass ())
406 obj
->set ("pass", get_id_value_for_pass (optinfo
->get_pass ()));
408 profile_count count
= optinfo
->get_count ();
409 if (count
.initialized_p ())
410 obj
->set ("count", profile_count_to_json (count
));
412 /* Record any location, handling the case where of an UNKNOWN_LOCATION
413 within an inlined block. */
414 location_t loc
= optinfo
->get_location_t ();
415 if (get_pure_location (line_table
, loc
) != UNKNOWN_LOCATION
)
417 // TOOD: record the location (just caret for now)
418 // TODO: start/finish also?
419 obj
->set ("location", location_to_json (loc
));
422 if (current_function_decl
)
424 const char *fnname
= get_fnname_from_decl (current_function_decl
);
425 obj
->set ("function", new json::string (fnname
));
428 if (loc
!= UNKNOWN_LOCATION
)
429 obj
->set ("inlining_chain", inlining_chain_to_json (loc
));
434 /* Add a json description of PASS and its siblings to ARR, recursing into
435 child passes (adding their descriptions within a "children" array). */
438 optrecord_json_writer::add_pass_list (json::array
*arr
, opt_pass
*pass
)
442 json::object
*pass_obj
= pass_to_json (pass
);
443 arr
->append (pass_obj
);
446 json::array
*sub
= new json::array ();
447 pass_obj
->set ("children", sub
);
448 add_pass_list (sub
, pass
->sub
);
455 /* File-level interface to rest of compiler (to avoid exposing
456 class optrecord_json_writer outside of this file). */
458 static optrecord_json_writer
*the_json_writer
;
460 /* Perform startup activity for -fsave-optimization-record. */
463 optimization_records_start ()
465 /* Bail if recording not enabled. */
466 if (!flag_save_optimization_record
)
469 the_json_writer
= new optrecord_json_writer ();
472 /* Perform cleanup activity for -fsave-optimization-record.
474 Currently, the file is written out here in one go, before cleaning
478 optimization_records_finish ()
480 /* Bail if recording not enabled. */
481 if (!the_json_writer
)
484 the_json_writer
->write ();
486 delete the_json_writer
;
487 the_json_writer
= NULL
;
490 /* Did the user request optimization records to be written out? */
493 optimization_records_enabled_p ()
495 return the_json_writer
!= NULL
;
498 /* If optimization records were requested, then add a record for OPTINFO
499 to the queue of records to be written. */
502 optimization_records_maybe_record_optinfo (const optinfo
*optinfo
)
504 /* Bail if recording not enabled. */
505 if (!the_json_writer
)
508 the_json_writer
->add_record (optinfo
);
511 /* Handling for the end of a dump scope for the
512 optimization records sink. */
515 optimization_records_maybe_pop_dump_scope ()
517 /* Bail if recording not enabled. */
518 if (!the_json_writer
)
521 the_json_writer
->pop_scope ();
528 /* Verify that we can build a JSON optimization record from dump_*
532 test_building_json_from_dump_calls ()
534 temp_dump_context
tmp (true, true, MSG_NOTE
);
536 dump_printf_loc (MSG_NOTE
, loc
, "test of tree: ");
537 dump_generic_expr (MSG_NOTE
, TDF_SLIM
, integer_zero_node
);
538 optinfo
*info
= tmp
.get_pending_optinfo ();
539 ASSERT_TRUE (info
!= NULL
);
540 ASSERT_EQ (info
->num_items (), 2);
542 optrecord_json_writer writer
;
543 json::object
*json_obj
= writer
.optinfo_to_json (info
);
544 ASSERT_TRUE (json_obj
!= NULL
);
546 /* Verify that the json is sane. */
548 json_obj
->print (&pp
);
549 const char *json_str
= pp_formatted_text (&pp
);
550 ASSERT_STR_CONTAINS (json_str
, "impl_location");
551 ASSERT_STR_CONTAINS (json_str
, "\"kind\": \"note\"");
552 ASSERT_STR_CONTAINS (json_str
,
553 "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
557 /* Run all of the selftests within this file. */
560 optinfo_emit_json_cc_tests ()
562 test_building_json_from_dump_calls ();
565 } // namespace selftest
567 #endif /* CHECKING_P */