libcpp, c, middle-end: Optimize initializers using #embed in C
[official-gcc.git] / gcc / optinfo-emit-json.cc
blob87a05a72dd3c0750a1e15f806764899133dc57d4
1 /* Emit optimization information as JSON files.
2 Copyright (C) 2018-2024 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 #define INCLUDE_MEMORY
23 #include "system.h"
24 #include "coretypes.h"
26 #include "backend.h"
27 #include "tree.h"
28 #include "gimple.h"
29 #include "diagnostic-core.h"
31 #include "profile.h"
32 #include "output.h"
33 #include "tree-pass.h"
35 #include "optinfo.h"
36 #include "optinfo-emit-json.h"
37 #include "json.h"
38 #include "pretty-print.h"
39 #include "tree-pretty-print.h"
40 #include "gimple-pretty-print.h"
41 #include "cgraph.h"
43 #include "langhooks.h"
44 #include "version.h"
45 #include "context.h"
46 #include "pass_manager.h"
47 #include "selftest.h"
48 #include "dump-context.h"
49 #include <zlib.h>
51 /* optrecord_json_writer's ctor. Populate the top-level parts of the
52 in-memory JSON representation. */
54 optrecord_json_writer::optrecord_json_writer ()
55 : m_root_tuple (NULL), m_scopes ()
57 m_root_tuple = new json::array ();
59 /* Populate with metadata; compare with toplev.cc: print_version. */
60 json::object *metadata = new json::object ();
61 m_root_tuple->append (metadata);
62 metadata->set_string ("format", "1");
63 json::object *generator = new json::object ();
64 metadata->set ("generator", generator);
65 generator->set_string ("name", lang_hooks.name);
66 generator->set_string ("pkgversion", pkgversion_string);
67 generator->set_string ("version", version_string);
68 /* TARGET_NAME is passed in by the Makefile. */
69 generator->set_string ("target", TARGET_NAME);
71 /* TODO: capture command-line?
72 see gen_producer_string in dwarf2out.cc (currently static). */
74 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
76 json::array *passes = new json::array ();
77 m_root_tuple->append (passes);
79 /* Call add_pass_list for all of the pass lists. */
81 #define DEF_PASS_LIST(LIST) \
82 add_pass_list (passes, g->get_passes ()->LIST);
83 GCC_PASS_LISTS
84 #undef DEF_PASS_LIST
87 json::array *records = new json::array ();
88 m_root_tuple->append (records);
90 m_scopes.safe_push (records);
93 /* optrecord_json_writer's ctor.
94 Delete the in-memory JSON representation. */
96 optrecord_json_writer::~optrecord_json_writer ()
98 delete m_root_tuple;
101 /* Choose an appropriate filename, and write the saved records to it. */
103 void
104 optrecord_json_writer::write () const
106 pretty_printer pp;
107 m_root_tuple->print (&pp, false);
109 bool emitted_error = false;
110 char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
111 gzFile outfile = gzopen (filename, "w");
112 if (outfile == NULL)
114 error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
115 filename); // FIXME: more info?
116 goto cleanup;
119 if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
121 int tmp;
122 error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
123 filename, gzerror (outfile, &tmp));
124 emitted_error = true;
127 cleanup:
128 if (outfile)
129 if (gzclose (outfile) != Z_OK)
130 if (!emitted_error)
131 error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
132 filename);
134 free (filename);
137 /* Add a record for OPTINFO to the queue of records to be written. */
139 void
140 optrecord_json_writer::add_record (const optinfo *optinfo)
142 json::object *obj = optinfo_to_json (optinfo);
144 add_record (obj);
146 /* Potentially push the scope. */
147 if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
149 json::array *children = new json::array ();
150 obj->set ("children", children);
151 m_scopes.safe_push (children);
155 /* Private methods of optrecord_json_writer. */
157 /* Add record OBJ to the innermost scope. */
159 void
160 optrecord_json_writer::add_record (json::object *obj)
162 /* Add to innermost scope. */
163 gcc_assert (m_scopes.length () > 0);
164 m_scopes[m_scopes.length () - 1]->append (obj);
167 /* Pop the innermost scope. */
169 void
170 optrecord_json_writer::pop_scope ()
172 m_scopes.pop ();
174 /* We should never pop the top-level records array. */
175 gcc_assert (m_scopes.length () > 0);
178 /* Create a JSON object representing LOC. */
180 json::object *
181 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
183 json::object *obj = new json::object ();
184 obj->set_string ("file", loc.m_file);
185 obj->set_integer ("line", loc.m_line);
186 if (loc.m_function)
187 obj->set_string ("function", loc.m_function);
188 return obj;
191 /* Create a JSON object representing LOC. */
193 json::object *
194 optrecord_json_writer::location_to_json (location_t loc)
196 gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
197 expanded_location exploc = expand_location (loc);
198 json::object *obj = new json::object ();
199 obj->set_string ("file", exploc.file);
200 obj->set_integer ("line", exploc.line);
201 obj->set_integer ("column", exploc.column);
202 return obj;
205 /* Create a JSON object representing COUNT. */
207 json::object *
208 optrecord_json_writer::profile_count_to_json (profile_count count)
210 json::object *obj = new json::object ();
211 obj->set_integer ("value", count.to_gcov_type ());
212 obj->set_string ("quality", profile_quality_as_string (count.quality ()));
213 return obj;
216 /* Get a string for use when referring to PASS in the saved optimization
217 records. */
219 json::string *
220 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
222 pretty_printer pp;
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. */
230 json::object *
231 optrecord_json_writer::pass_to_json (opt_pass *pass)
233 json::object *obj = new json::object ();
234 const char *type = NULL;
235 switch (pass->type)
237 default:
238 gcc_unreachable ();
239 case GIMPLE_PASS:
240 type = "gimple";
241 break;
242 case RTL_PASS:
243 type = "rtl";
244 break;
245 case SIMPLE_IPA_PASS:
246 type = "simple_ipa";
247 break;
248 case IPA_PASS:
249 type = "ipa";
250 break;
252 obj->set ("id", get_id_value_for_pass (pass));
253 obj->set_string ("type", type);
254 obj->set_string ("name", 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_string (optgroup->name);
265 obj->set_integer ("num", pass->static_pass_number);
266 return obj;
269 /* Create a JSON array for LOC representing the chain of inlining
270 locations.
271 Compare with lhd_print_error_function and cp_print_error_function. */
273 json::value *
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)
282 location_t *locus;
283 tree block = abstract_origin;
285 locus = &BLOCK_SOURCE_LOCATION (block);
286 tree fndecl = NULL;
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)
294 fndecl = ao;
295 break;
297 else if (TREE_CODE (ao) != BLOCK)
298 break;
300 block = BLOCK_SUPERCONTEXT (block);
302 if (fndecl)
303 abstract_origin = block;
304 else
306 while (block && TREE_CODE (block) == BLOCK)
307 block = BLOCK_SUPERCONTEXT (block);
309 if (block && TREE_CODE (block) == FUNCTION_DECL)
310 fndecl = block;
311 abstract_origin = NULL;
313 if (fndecl)
315 json::object *obj = new json::object ();
316 const char *printable_name
317 = lang_hooks.decl_printable_name (fndecl, 2);
318 obj->set_string ("fndecl", printable_name);
319 if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
320 obj->set ("site", location_to_json (*locus));
321 array->append (obj);
325 return array;
328 /* Create a JSON object representing OPTINFO. */
330 json::object *
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_string ("kind", 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 ())
347 default:
348 gcc_unreachable ();
349 case OPTINFO_ITEM_KIND_TEXT:
351 message->append_string (item->get_text ());
353 break;
354 case OPTINFO_ITEM_KIND_TREE:
356 json::object *json_item = new json::object ();
357 json_item->set_string ("expr", 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);
366 break;
367 case OPTINFO_ITEM_KIND_GIMPLE:
369 json::object *json_item = new json::object ();
370 json_item->set_string ("stmt", 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);
379 break;
380 case OPTINFO_ITEM_KIND_SYMTAB_NODE:
382 json::object *json_item = new json::object ();
383 json_item->set_string ("symtab_node", 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);
391 break;
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)
414 const char *fnname
415 = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
416 obj->set_string ("function", fnname);
419 if (loc != UNKNOWN_LOCATION)
420 obj->set ("inlining_chain", inlining_chain_to_json (loc));
422 return obj;
425 /* Add a json description of PASS and its siblings to ARR, recursing into
426 child passes (adding their descriptions within a "children" array). */
428 void
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);
435 if (pass->sub)
437 json::array *sub = new json::array ();
438 pass_obj->set ("children", sub);
439 add_pass_list (sub, pass->sub);
441 pass = pass->next;
443 while (pass);
446 #if CHECKING_P
448 namespace selftest {
450 /* Verify that we can build a JSON optimization record from dump_*
451 calls. */
453 static void
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. */
469 pretty_printer pp;
470 json_obj->print (&pp, false);
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\"}]");
476 delete json_obj;
479 /* Run all of the selftests within this file. */
481 void
482 optinfo_emit_json_cc_tests ()
484 test_building_json_from_dump_calls ();
487 } // namespace selftest
489 #endif /* CHECKING_P */