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 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 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
)));
212 /* Create a JSON object representing COUNT. */
215 optrecord_json_writer::profile_count_to_json (profile_count count
)
217 json::object
*obj
= new json::object ();
218 obj
->set ("value", new json::number (count
.to_gcov_type ()));
220 new json::string (profile_quality_as_string (count
.quality ())));
224 /* Get a string for use when referring to PASS in the saved optimization
228 optrecord_json_writer::get_id_value_for_pass (opt_pass
*pass
)
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
));
236 /* Create a JSON object representing PASS. */
239 optrecord_json_writer::pass_to_json (opt_pass
*pass
)
241 json::object
*obj
= new json::object ();
242 const char *type
= NULL
;
253 case SIMPLE_IPA_PASS
:
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. */
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
));
273 obj
->set ("num", new json::number (pass
->static_pass_number
));
277 /* Create a JSON array for LOC representing the chain of inlining
279 Compare with lhd_print_error_function and cp_print_error_function. */
282 optrecord_json_writer::inlining_chain_to_json (location_t loc
)
284 json::array
*array
= new json::array ();
286 tree abstract_origin
= LOCATION_BLOCK (loc
);
288 while (abstract_origin
)
291 tree block
= abstract_origin
;
293 locus
= &BLOCK_SOURCE_LOCATION (block
);
295 block
= BLOCK_SUPERCONTEXT (block
);
296 while (block
&& TREE_CODE (block
) == BLOCK
297 && BLOCK_ABSTRACT_ORIGIN (block
))
299 tree ao
= BLOCK_ABSTRACT_ORIGIN (block
);
301 while (TREE_CODE (ao
) == BLOCK
302 && BLOCK_ABSTRACT_ORIGIN (ao
)
303 && BLOCK_ABSTRACT_ORIGIN (ao
) != ao
)
304 ao
= BLOCK_ABSTRACT_ORIGIN (ao
);
306 if (TREE_CODE (ao
) == FUNCTION_DECL
)
311 else if (TREE_CODE (ao
) != BLOCK
)
314 block
= BLOCK_SUPERCONTEXT (block
);
317 abstract_origin
= block
;
320 while (block
&& TREE_CODE (block
) == BLOCK
)
321 block
= BLOCK_SUPERCONTEXT (block
);
323 if (block
&& TREE_CODE (block
) == FUNCTION_DECL
)
325 abstract_origin
= NULL
;
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
));
342 /* Create a JSON object representing OPTINFO. */
345 optrecord_json_writer::optinfo_to_json (const optinfo
*optinfo
)
347 json::object
*obj
= new json::object ();
349 obj
->set ("impl_location",
350 impl_location_to_json (optinfo
->get_impl_location ()));
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
++)
358 const optinfo_item
*item
= optinfo
->get_item (i
);
359 switch (item
->get_kind ())
363 case OPTINFO_ITEM_KIND_TEXT
:
365 message
->append (new json::string (item
->get_text ()));
368 case OPTINFO_ITEM_KIND_TREE
:
370 json::object
*json_item
= new json::object ();
371 json_item
->set ("expr", new json::string (item
->get_text ()));
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 ()));
377 message
->append (json_item
);
380 case OPTINFO_ITEM_KIND_GIMPLE
:
382 json::object
*json_item
= new json::object ();
383 json_item
->set ("stmt", new json::string (item
->get_text ()));
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 ()));
389 message
->append (json_item
);
392 case OPTINFO_ITEM_KIND_SYMTAB_NODE
:
394 json::object
*json_item
= new json::object ();
395 json_item
->set ("symtab_node", new json::string (item
->get_text ()));
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
);
406 if (optinfo
->get_pass ())
407 obj
->set ("pass", get_id_value_for_pass (optinfo
->get_pass ()));
409 profile_count count
= optinfo
->get_count ();
410 if (count
.initialized_p ())
411 obj
->set ("count", profile_count_to_json (count
));
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
)
418 // TOOD: record the location (just caret for now)
419 // TODO: start/finish also?
420 obj
->set ("location", location_to_json (loc
));
423 if (current_function_decl
)
425 const char *fnname
= get_fnname_from_decl (current_function_decl
);
426 obj
->set ("function", new json::string (fnname
));
429 if (loc
!= UNKNOWN_LOCATION
)
430 obj
->set ("inlining_chain", inlining_chain_to_json (loc
));
435 /* Add a json description of PASS and its siblings to ARR, recursing into
436 child passes (adding their descriptions within a "children" array). */
439 optrecord_json_writer::add_pass_list (json::array
*arr
, opt_pass
*pass
)
443 json::object
*pass_obj
= pass_to_json (pass
);
444 arr
->append (pass_obj
);
447 json::array
*sub
= new json::array ();
448 pass_obj
->set ("children", sub
);
449 add_pass_list (sub
, pass
->sub
);
456 /* File-level interface to rest of compiler (to avoid exposing
457 class optrecord_json_writer outside of this file). */
459 static optrecord_json_writer
*the_json_writer
;
461 /* Perform startup activity for -fsave-optimization-record. */
464 optimization_records_start ()
466 /* Bail if recording not enabled. */
467 if (!flag_save_optimization_record
)
470 the_json_writer
= new optrecord_json_writer ();
473 /* Perform cleanup activity for -fsave-optimization-record.
475 Currently, the file is written out here in one go, before cleaning
479 optimization_records_finish ()
481 /* Bail if recording not enabled. */
482 if (!the_json_writer
)
485 the_json_writer
->write ();
487 delete the_json_writer
;
488 the_json_writer
= NULL
;
491 /* Did the user request optimization records to be written out? */
494 optimization_records_enabled_p ()
496 return the_json_writer
!= NULL
;
499 /* If optimization records were requested, then add a record for OPTINFO
500 to the queue of records to be written. */
503 optimization_records_maybe_record_optinfo (const optinfo
*optinfo
)
505 /* Bail if recording not enabled. */
506 if (!the_json_writer
)
509 the_json_writer
->add_record (optinfo
);
512 /* Handling for the end of a dump scope for the
513 optimization records sink. */
516 optimization_records_maybe_pop_dump_scope ()
518 /* Bail if recording not enabled. */
519 if (!the_json_writer
)
522 the_json_writer
->pop_scope ();
529 /* Verify that we can build a JSON optimization record from dump_*
533 test_building_json_from_dump_calls ()
535 temp_dump_context
tmp (true);
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);
543 optrecord_json_writer writer
;
544 json::object
*json_obj
= writer
.optinfo_to_json (info
);
545 ASSERT_TRUE (json_obj
!= NULL
);
547 /* Verify that the json is sane. */
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\"}]");
558 /* Run all of the selftests within this file. */
561 optinfo_emit_json_cc_tests ()
563 test_building_json_from_dump_calls ();
566 } // namespace selftest
568 #endif /* CHECKING_P */