1 /* Emit optimization information as JSON files.
2 Copyright (C) 2018-2022 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"
50 /* optrecord_json_writer's ctor. Populate the top-level parts of the
51 in-memory JSON representation. */
53 optrecord_json_writer::optrecord_json_writer ()
54 : m_root_tuple (NULL
), m_scopes ()
56 m_root_tuple
= new json::array ();
58 /* Populate with metadata; compare with toplev.cc: print_version. */
59 json::object
*metadata
= new json::object ();
60 m_root_tuple
->append (metadata
);
61 metadata
->set ("format", new json::string ("1"));
62 json::object
*generator
= new json::object ();
63 metadata
->set ("generator", generator
);
64 generator
->set ("name", new json::string (lang_hooks
.name
));
65 generator
->set ("pkgversion", new json::string (pkgversion_string
));
66 generator
->set ("version", new json::string (version_string
));
67 /* TARGET_NAME is passed in by the Makefile. */
68 generator
->set ("target", new json::string (TARGET_NAME
));
70 /* TODO: capture command-line?
71 see gen_producer_string in dwarf2out.cc (currently static). */
73 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
75 json::array
*passes
= new json::array ();
76 m_root_tuple
->append (passes
);
78 /* Call add_pass_list for all of the pass lists. */
80 #define DEF_PASS_LIST(LIST) \
81 add_pass_list (passes, g->get_passes ()->LIST);
86 json::array
*records
= new json::array ();
87 m_root_tuple
->append (records
);
89 m_scopes
.safe_push (records
);
92 /* optrecord_json_writer's ctor.
93 Delete the in-memory JSON representation. */
95 optrecord_json_writer::~optrecord_json_writer ()
100 /* Choose an appropriate filename, and write the saved records to it. */
103 optrecord_json_writer::write () const
106 m_root_tuple
->print (&pp
);
108 bool emitted_error
= false;
109 char *filename
= concat (dump_base_name
, ".opt-record.json.gz", NULL
);
110 gzFile outfile
= gzopen (filename
, "w");
113 error_at (UNKNOWN_LOCATION
, "cannot open file %qs for writing optimization records",
114 filename
); // FIXME: more info?
118 if (gzputs (outfile
, pp_formatted_text (&pp
)) <= 0)
121 error_at (UNKNOWN_LOCATION
, "error writing optimization records to %qs: %s",
122 filename
, gzerror (outfile
, &tmp
));
123 emitted_error
= true;
128 if (gzclose (outfile
) != Z_OK
)
130 error_at (UNKNOWN_LOCATION
, "error closing optimization records %qs",
136 /* Add a record for OPTINFO to the queue of records to be written. */
139 optrecord_json_writer::add_record (const optinfo
*optinfo
)
141 json::object
*obj
= optinfo_to_json (optinfo
);
145 /* Potentially push the scope. */
146 if (optinfo
->get_kind () == OPTINFO_KIND_SCOPE
)
148 json::array
*children
= new json::array ();
149 obj
->set ("children", children
);
150 m_scopes
.safe_push (children
);
154 /* Private methods of optrecord_json_writer. */
156 /* Add record OBJ to the innermost scope. */
159 optrecord_json_writer::add_record (json::object
*obj
)
161 /* Add to innermost scope. */
162 gcc_assert (m_scopes
.length () > 0);
163 m_scopes
[m_scopes
.length () - 1]->append (obj
);
166 /* Pop the innermost scope. */
169 optrecord_json_writer::pop_scope ()
173 /* We should never pop the top-level records array. */
174 gcc_assert (m_scopes
.length () > 0);
177 /* Create a JSON object representing LOC. */
180 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc
)
182 json::object
*obj
= new json::object ();
183 obj
->set ("file", new json::string (loc
.m_file
));
184 obj
->set ("line", new json::integer_number (loc
.m_line
));
186 obj
->set ("function", new json::string (loc
.m_function
));
190 /* Create a JSON object representing LOC. */
193 optrecord_json_writer::location_to_json (location_t loc
)
195 gcc_assert (LOCATION_LOCUS (loc
) != UNKNOWN_LOCATION
);
196 expanded_location exploc
= expand_location (loc
);
197 json::object
*obj
= new json::object ();
198 obj
->set ("file", new json::string (exploc
.file
));
199 obj
->set ("line", new json::integer_number (exploc
.line
));
200 obj
->set ("column", new json::integer_number (exploc
.column
));
204 /* Create a JSON object representing COUNT. */
207 optrecord_json_writer::profile_count_to_json (profile_count count
)
209 json::object
*obj
= new json::object ();
210 obj
->set ("value", new json::integer_number (count
.to_gcov_type ()));
212 new json::string (profile_quality_as_string (count
.quality ())));
216 /* Get a string for use when referring to PASS in the saved optimization
220 optrecord_json_writer::get_id_value_for_pass (opt_pass
*pass
)
223 /* this is host-dependent, but will be consistent for a given host. */
224 pp_pointer (&pp
, static_cast<void *> (pass
));
225 return new json::string (pp_formatted_text (&pp
));
228 /* Create a JSON object representing PASS. */
231 optrecord_json_writer::pass_to_json (opt_pass
*pass
)
233 json::object
*obj
= new json::object ();
234 const char *type
= NULL
;
245 case SIMPLE_IPA_PASS
:
252 obj
->set ("id", get_id_value_for_pass (pass
));
253 obj
->set ("type", new json::string (type
));
254 obj
->set ("name", new json::string (pass
->name
));
255 /* Represent the optgroup flags as an array. */
257 json::array
*optgroups
= new json::array ();
258 obj
->set ("optgroups", optgroups
);
259 for (const kv_pair
<optgroup_flags_t
> *optgroup
= optgroup_options
;
260 optgroup
->name
!= NULL
; optgroup
++)
261 if (optgroup
->value
!= OPTGROUP_ALL
262 && (pass
->optinfo_flags
& optgroup
->value
))
263 optgroups
->append (new json::string (optgroup
->name
));
265 obj
->set ("num", new json::integer_number (pass
->static_pass_number
));
269 /* Create a JSON array for LOC representing the chain of inlining
271 Compare with lhd_print_error_function and cp_print_error_function. */
274 optrecord_json_writer::inlining_chain_to_json (location_t loc
)
276 json::array
*array
= new json::array ();
278 tree abstract_origin
= LOCATION_BLOCK (loc
);
280 while (abstract_origin
)
283 tree block
= abstract_origin
;
285 locus
= &BLOCK_SOURCE_LOCATION (block
);
287 block
= BLOCK_SUPERCONTEXT (block
);
288 while (block
&& TREE_CODE (block
) == BLOCK
289 && BLOCK_ABSTRACT_ORIGIN (block
))
291 tree ao
= BLOCK_ABSTRACT_ORIGIN (block
);
292 if (TREE_CODE (ao
) == FUNCTION_DECL
)
297 else if (TREE_CODE (ao
) != BLOCK
)
300 block
= BLOCK_SUPERCONTEXT (block
);
303 abstract_origin
= block
;
306 while (block
&& TREE_CODE (block
) == BLOCK
)
307 block
= BLOCK_SUPERCONTEXT (block
);
309 if (block
&& TREE_CODE (block
) == FUNCTION_DECL
)
311 abstract_origin
= NULL
;
315 json::object
*obj
= new json::object ();
316 const char *printable_name
317 = lang_hooks
.decl_printable_name (fndecl
, 2);
318 obj
->set ("fndecl", new json::string (printable_name
));
319 if (LOCATION_LOCUS (*locus
) != UNKNOWN_LOCATION
)
320 obj
->set ("site", location_to_json (*locus
));
328 /* Create a JSON object representing OPTINFO. */
331 optrecord_json_writer::optinfo_to_json (const optinfo
*optinfo
)
333 json::object
*obj
= new json::object ();
335 obj
->set ("impl_location",
336 impl_location_to_json (optinfo
->get_impl_location ()));
338 const char *kind_str
= optinfo_kind_to_string (optinfo
->get_kind ());
339 obj
->set ("kind", new json::string (kind_str
));
340 json::array
*message
= new json::array ();
341 obj
->set ("message", message
);
342 for (unsigned i
= 0; i
< optinfo
->num_items (); i
++)
344 const optinfo_item
*item
= optinfo
->get_item (i
);
345 switch (item
->get_kind ())
349 case OPTINFO_ITEM_KIND_TEXT
:
351 message
->append (new json::string (item
->get_text ()));
354 case OPTINFO_ITEM_KIND_TREE
:
356 json::object
*json_item
= new json::object ();
357 json_item
->set ("expr", new json::string (item
->get_text ()));
359 /* Capture any location for the node. */
360 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
361 json_item
->set ("location",
362 location_to_json (item
->get_location ()));
364 message
->append (json_item
);
367 case OPTINFO_ITEM_KIND_GIMPLE
:
369 json::object
*json_item
= new json::object ();
370 json_item
->set ("stmt", new json::string (item
->get_text ()));
372 /* Capture any location for the stmt. */
373 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
374 json_item
->set ("location",
375 location_to_json (item
->get_location ()));
377 message
->append (json_item
);
380 case OPTINFO_ITEM_KIND_SYMTAB_NODE
:
382 json::object
*json_item
= new json::object ();
383 json_item
->set ("symtab_node", new json::string (item
->get_text ()));
385 /* Capture any location for the node. */
386 if (LOCATION_LOCUS (item
->get_location ()) != UNKNOWN_LOCATION
)
387 json_item
->set ("location",
388 location_to_json (item
->get_location ()));
389 message
->append (json_item
);
395 if (optinfo
->get_pass ())
396 obj
->set ("pass", get_id_value_for_pass (optinfo
->get_pass ()));
398 profile_count count
= optinfo
->get_count ();
399 if (count
.initialized_p ())
400 obj
->set ("count", profile_count_to_json (count
));
402 /* Record any location, handling the case where of an UNKNOWN_LOCATION
403 within an inlined block. */
404 location_t loc
= optinfo
->get_location_t ();
405 if (get_pure_location (line_table
, loc
) != UNKNOWN_LOCATION
)
407 // TOOD: record the location (just caret for now)
408 // TODO: start/finish also?
409 obj
->set ("location", location_to_json (loc
));
412 if (current_function_decl
)
415 = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl
));
416 obj
->set ("function", new json::string (fnname
));
419 if (loc
!= UNKNOWN_LOCATION
)
420 obj
->set ("inlining_chain", inlining_chain_to_json (loc
));
425 /* Add a json description of PASS and its siblings to ARR, recursing into
426 child passes (adding their descriptions within a "children" array). */
429 optrecord_json_writer::add_pass_list (json::array
*arr
, opt_pass
*pass
)
433 json::object
*pass_obj
= pass_to_json (pass
);
434 arr
->append (pass_obj
);
437 json::array
*sub
= new json::array ();
438 pass_obj
->set ("children", sub
);
439 add_pass_list (sub
, pass
->sub
);
450 /* Verify that we can build a JSON optimization record from dump_*
454 test_building_json_from_dump_calls ()
456 temp_dump_context
tmp (true, true, MSG_NOTE
);
457 dump_user_location_t loc
;
458 dump_printf_loc (MSG_NOTE
, loc
, "test of tree: ");
459 dump_generic_expr (MSG_NOTE
, TDF_SLIM
, integer_zero_node
);
460 optinfo
*info
= tmp
.get_pending_optinfo ();
461 ASSERT_TRUE (info
!= NULL
);
462 ASSERT_EQ (info
->num_items (), 2);
464 optrecord_json_writer writer
;
465 json::object
*json_obj
= writer
.optinfo_to_json (info
);
466 ASSERT_TRUE (json_obj
!= NULL
);
468 /* Verify that the json is sane. */
470 json_obj
->print (&pp
);
471 const char *json_str
= pp_formatted_text (&pp
);
472 ASSERT_STR_CONTAINS (json_str
, "impl_location");
473 ASSERT_STR_CONTAINS (json_str
, "\"kind\": \"note\"");
474 ASSERT_STR_CONTAINS (json_str
,
475 "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
479 /* Run all of the selftests within this file. */
482 optinfo_emit_json_cc_tests ()
484 test_building_json_from_dump_calls ();
487 } // namespace selftest
489 #endif /* CHECKING_P */