131
|
1 /* Emit optimization information as JSON files.
|
145
|
2 Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
131
|
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
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.
|
|
11
|
|
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.
|
|
16
|
|
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/>. */
|
|
20
|
|
21 #include "config.h"
|
|
22 #include "system.h"
|
|
23 #include "coretypes.h"
|
|
24
|
|
25 #include "backend.h"
|
|
26 #include "tree.h"
|
|
27 #include "gimple.h"
|
|
28 #include "diagnostic-core.h"
|
|
29
|
|
30 #include "profile.h"
|
|
31 #include "output.h"
|
|
32 #include "tree-pass.h"
|
|
33
|
|
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"
|
|
41
|
|
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"
|
145
|
48 #include <zlib.h>
|
131
|
49
|
|
50 /* optrecord_json_writer's ctor. Populate the top-level parts of the
|
|
51 in-memory JSON representation. */
|
|
52
|
|
53 optrecord_json_writer::optrecord_json_writer ()
|
|
54 : m_root_tuple (NULL), m_scopes ()
|
|
55 {
|
|
56 m_root_tuple = new json::array ();
|
|
57
|
|
58 /* Populate with metadata; compare with toplev.c: 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));
|
|
69
|
|
70 /* TODO: capture command-line?
|
|
71 see gen_producer_string in dwarf2out.c (currently static). */
|
|
72
|
|
73 /* TODO: capture "any plugins?" flag (or the plugins themselves). */
|
|
74
|
|
75 json::array *passes = new json::array ();
|
|
76 m_root_tuple->append (passes);
|
|
77
|
|
78 /* Call add_pass_list for all of the pass lists. */
|
|
79 {
|
|
80 #define DEF_PASS_LIST(LIST) \
|
|
81 add_pass_list (passes, g->get_passes ()->LIST);
|
|
82 GCC_PASS_LISTS
|
|
83 #undef DEF_PASS_LIST
|
|
84 }
|
|
85
|
|
86 json::array *records = new json::array ();
|
|
87 m_root_tuple->append (records);
|
|
88
|
|
89 m_scopes.safe_push (records);
|
|
90 }
|
|
91
|
|
92 /* optrecord_json_writer's ctor.
|
|
93 Delete the in-memory JSON representation. */
|
|
94
|
|
95 optrecord_json_writer::~optrecord_json_writer ()
|
|
96 {
|
|
97 delete m_root_tuple;
|
|
98 }
|
|
99
|
|
100 /* Choose an appropriate filename, and write the saved records to it. */
|
|
101
|
|
102 void
|
|
103 optrecord_json_writer::write () const
|
|
104 {
|
145
|
105 pretty_printer pp;
|
|
106 m_root_tuple->print (&pp);
|
|
107
|
|
108 bool emitted_error = false;
|
|
109 char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
|
|
110 gzFile outfile = gzopen (filename, "w");
|
|
111 if (outfile == NULL)
|
131
|
112 {
|
145
|
113 error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
|
|
114 filename); // FIXME: more info?
|
|
115 goto cleanup;
|
131
|
116 }
|
145
|
117
|
|
118 if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
|
|
119 {
|
|
120 int tmp;
|
|
121 error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
|
|
122 filename, gzerror (outfile, &tmp));
|
|
123 emitted_error = true;
|
|
124 }
|
|
125
|
|
126 cleanup:
|
|
127 if (outfile)
|
|
128 if (gzclose (outfile) != Z_OK)
|
|
129 if (!emitted_error)
|
|
130 error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
|
|
131 filename);
|
|
132
|
131
|
133 free (filename);
|
|
134 }
|
|
135
|
|
136 /* Add a record for OPTINFO to the queue of records to be written. */
|
|
137
|
|
138 void
|
|
139 optrecord_json_writer::add_record (const optinfo *optinfo)
|
|
140 {
|
|
141 json::object *obj = optinfo_to_json (optinfo);
|
|
142
|
|
143 add_record (obj);
|
|
144
|
|
145 /* Potentially push the scope. */
|
|
146 if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
|
|
147 {
|
|
148 json::array *children = new json::array ();
|
|
149 obj->set ("children", children);
|
|
150 m_scopes.safe_push (children);
|
|
151 }
|
|
152 }
|
|
153
|
|
154 /* Private methods of optrecord_json_writer. */
|
|
155
|
|
156 /* Add record OBJ to the the innermost scope. */
|
|
157
|
|
158 void
|
|
159 optrecord_json_writer::add_record (json::object *obj)
|
|
160 {
|
|
161 /* Add to innermost scope. */
|
|
162 gcc_assert (m_scopes.length () > 0);
|
|
163 m_scopes[m_scopes.length () - 1]->append (obj);
|
|
164 }
|
|
165
|
|
166 /* Pop the innermost scope. */
|
|
167
|
|
168 void
|
|
169 optrecord_json_writer::pop_scope ()
|
|
170 {
|
|
171 m_scopes.pop ();
|
145
|
172
|
|
173 /* We should never pop the top-level records array. */
|
|
174 gcc_assert (m_scopes.length () > 0);
|
131
|
175 }
|
|
176
|
|
177 /* Create a JSON object representing LOC. */
|
|
178
|
|
179 json::object *
|
|
180 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
|
|
181 {
|
|
182 json::object *obj = new json::object ();
|
|
183 obj->set ("file", new json::string (loc.m_file));
|
145
|
184 obj->set ("line", new json::integer_number (loc.m_line));
|
131
|
185 if (loc.m_function)
|
|
186 obj->set ("function", new json::string (loc.m_function));
|
|
187 return obj;
|
|
188 }
|
|
189
|
|
190 /* Create a JSON object representing LOC. */
|
|
191
|
|
192 json::object *
|
|
193 optrecord_json_writer::location_to_json (location_t loc)
|
|
194 {
|
|
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));
|
145
|
199 obj->set ("line", new json::integer_number (exploc.line));
|
|
200 obj->set ("column", new json::integer_number (exploc.column));
|
131
|
201 return obj;
|
|
202 }
|
|
203
|
|
204 /* Create a JSON object representing COUNT. */
|
|
205
|
|
206 json::object *
|
|
207 optrecord_json_writer::profile_count_to_json (profile_count count)
|
|
208 {
|
|
209 json::object *obj = new json::object ();
|
145
|
210 obj->set ("value", new json::integer_number (count.to_gcov_type ()));
|
131
|
211 obj->set ("quality",
|
|
212 new json::string (profile_quality_as_string (count.quality ())));
|
|
213 return obj;
|
|
214 }
|
|
215
|
|
216 /* Get a string for use when referring to PASS in the saved optimization
|
|
217 records. */
|
|
218
|
|
219 json::string *
|
|
220 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
|
|
221 {
|
|
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));
|
|
226 }
|
|
227
|
|
228 /* Create a JSON object representing PASS. */
|
|
229
|
|
230 json::object *
|
|
231 optrecord_json_writer::pass_to_json (opt_pass *pass)
|
|
232 {
|
|
233 json::object *obj = new json::object ();
|
|
234 const char *type = NULL;
|
|
235 switch (pass->type)
|
|
236 {
|
|
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;
|
|
251 }
|
|
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. */
|
|
256 {
|
|
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));
|
|
264 }
|
145
|
265 obj->set ("num", new json::integer_number (pass->static_pass_number));
|
131
|
266 return obj;
|
|
267 }
|
|
268
|
|
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. */
|
|
272
|
|
273 json::value *
|
|
274 optrecord_json_writer::inlining_chain_to_json (location_t loc)
|
|
275 {
|
|
276 json::array *array = new json::array ();
|
|
277
|
|
278 tree abstract_origin = LOCATION_BLOCK (loc);
|
|
279
|
|
280 while (abstract_origin)
|
|
281 {
|
|
282 location_t *locus;
|
|
283 tree block = abstract_origin;
|
|
284
|
|
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))
|
|
290 {
|
|
291 tree ao = BLOCK_ABSTRACT_ORIGIN (block);
|
|
292 if (TREE_CODE (ao) == FUNCTION_DECL)
|
|
293 {
|
|
294 fndecl = ao;
|
|
295 break;
|
|
296 }
|
|
297 else if (TREE_CODE (ao) != BLOCK)
|
|
298 break;
|
|
299
|
|
300 block = BLOCK_SUPERCONTEXT (block);
|
|
301 }
|
|
302 if (fndecl)
|
|
303 abstract_origin = block;
|
|
304 else
|
|
305 {
|
|
306 while (block && TREE_CODE (block) == BLOCK)
|
|
307 block = BLOCK_SUPERCONTEXT (block);
|
|
308
|
|
309 if (block && TREE_CODE (block) == FUNCTION_DECL)
|
|
310 fndecl = block;
|
|
311 abstract_origin = NULL;
|
|
312 }
|
|
313 if (fndecl)
|
|
314 {
|
|
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));
|
|
321 array->append (obj);
|
|
322 }
|
|
323 }
|
|
324
|
|
325 return array;
|
|
326 }
|
|
327
|
|
328 /* Create a JSON object representing OPTINFO. */
|
|
329
|
|
330 json::object *
|
|
331 optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
|
|
332 {
|
|
333 json::object *obj = new json::object ();
|
|
334
|
|
335 obj->set ("impl_location",
|
|
336 impl_location_to_json (optinfo->get_impl_location ()));
|
|
337
|
|
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++)
|
|
343 {
|
|
344 const optinfo_item *item = optinfo->get_item (i);
|
|
345 switch (item->get_kind ())
|
|
346 {
|
|
347 default:
|
|
348 gcc_unreachable ();
|
|
349 case OPTINFO_ITEM_KIND_TEXT:
|
|
350 {
|
|
351 message->append (new json::string (item->get_text ()));
|
|
352 }
|
|
353 break;
|
|
354 case OPTINFO_ITEM_KIND_TREE:
|
|
355 {
|
|
356 json::object *json_item = new json::object ();
|
|
357 json_item->set ("expr", new json::string (item->get_text ()));
|
|
358
|
|
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 ()));
|
|
363
|
|
364 message->append (json_item);
|
|
365 }
|
|
366 break;
|
|
367 case OPTINFO_ITEM_KIND_GIMPLE:
|
|
368 {
|
|
369 json::object *json_item = new json::object ();
|
|
370 json_item->set ("stmt", new json::string (item->get_text ()));
|
|
371
|
|
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 ()));
|
|
376
|
|
377 message->append (json_item);
|
|
378 }
|
|
379 break;
|
|
380 case OPTINFO_ITEM_KIND_SYMTAB_NODE:
|
|
381 {
|
|
382 json::object *json_item = new json::object ();
|
|
383 json_item->set ("symtab_node", new json::string (item->get_text ()));
|
|
384
|
|
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);
|
|
390 }
|
|
391 break;
|
|
392 }
|
|
393 }
|
|
394
|
|
395 if (optinfo->get_pass ())
|
|
396 obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
|
|
397
|
|
398 profile_count count = optinfo->get_count ();
|
|
399 if (count.initialized_p ())
|
|
400 obj->set ("count", profile_count_to_json (count));
|
|
401
|
|
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)
|
|
406 {
|
|
407 // TOOD: record the location (just caret for now)
|
|
408 // TODO: start/finish also?
|
|
409 obj->set ("location", location_to_json (loc));
|
|
410 }
|
|
411
|
|
412 if (current_function_decl)
|
|
413 {
|
145
|
414 const char *fnname
|
|
415 = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
|
131
|
416 obj->set ("function", new json::string (fnname));
|
|
417 }
|
|
418
|
|
419 if (loc != UNKNOWN_LOCATION)
|
|
420 obj->set ("inlining_chain", inlining_chain_to_json (loc));
|
|
421
|
|
422 return obj;
|
|
423 }
|
|
424
|
|
425 /* Add a json description of PASS and its siblings to ARR, recursing into
|
|
426 child passes (adding their descriptions within a "children" array). */
|
|
427
|
|
428 void
|
|
429 optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
|
|
430 {
|
|
431 do
|
|
432 {
|
|
433 json::object *pass_obj = pass_to_json (pass);
|
|
434 arr->append (pass_obj);
|
|
435 if (pass->sub)
|
|
436 {
|
|
437 json::array *sub = new json::array ();
|
|
438 pass_obj->set ("children", sub);
|
|
439 add_pass_list (sub, pass->sub);
|
|
440 }
|
|
441 pass = pass->next;
|
|
442 }
|
|
443 while (pass);
|
|
444 }
|
|
445
|
|
446 #if CHECKING_P
|
|
447
|
|
448 namespace selftest {
|
|
449
|
|
450 /* Verify that we can build a JSON optimization record from dump_*
|
|
451 calls. */
|
|
452
|
|
453 static void
|
|
454 test_building_json_from_dump_calls ()
|
|
455 {
|
|
456 temp_dump_context tmp (true, true, MSG_NOTE);
|
145
|
457 dump_user_location_t loc;
|
131
|
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);
|
|
463
|
|
464 optrecord_json_writer writer;
|
|
465 json::object *json_obj = writer.optinfo_to_json (info);
|
|
466 ASSERT_TRUE (json_obj != NULL);
|
|
467
|
|
468 /* Verify that the json is sane. */
|
|
469 pretty_printer pp;
|
|
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\"}]");
|
|
476 delete json_obj;
|
|
477 }
|
|
478
|
|
479 /* Run all of the selftests within this file. */
|
|
480
|
|
481 void
|
|
482 optinfo_emit_json_cc_tests ()
|
|
483 {
|
|
484 test_building_json_from_dump_calls ();
|
|
485 }
|
|
486
|
|
487 } // namespace selftest
|
|
488
|
|
489 #endif /* CHECKING_P */
|