1 1.1 mrg /* Emit optimization information as JSON files. 2 1.1.1.3 mrg Copyright (C) 2018-2022 Free Software Foundation, Inc. 3 1.1 mrg Contributed by David Malcolm <dmalcolm (at) redhat.com>. 4 1.1 mrg 5 1.1 mrg This file is part of GCC. 6 1.1 mrg 7 1.1 mrg GCC is free software; you can redistribute it and/or modify it under 8 1.1 mrg the terms of the GNU General Public License as published by the Free 9 1.1 mrg Software Foundation; either version 3, or (at your option) any later 10 1.1 mrg version. 11 1.1 mrg 12 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 1.1 mrg for more details. 16 1.1 mrg 17 1.1 mrg You should have received a copy of the GNU General Public License 18 1.1 mrg along with GCC; see the file COPYING3. If not see 19 1.1 mrg <http://www.gnu.org/licenses/>. */ 20 1.1 mrg 21 1.1 mrg #include "config.h" 22 1.1 mrg #include "system.h" 23 1.1 mrg #include "coretypes.h" 24 1.1 mrg 25 1.1 mrg #include "backend.h" 26 1.1 mrg #include "tree.h" 27 1.1 mrg #include "gimple.h" 28 1.1 mrg #include "diagnostic-core.h" 29 1.1 mrg 30 1.1 mrg #include "profile.h" 31 1.1 mrg #include "output.h" 32 1.1 mrg #include "tree-pass.h" 33 1.1 mrg 34 1.1 mrg #include "optinfo.h" 35 1.1 mrg #include "optinfo-emit-json.h" 36 1.1 mrg #include "json.h" 37 1.1 mrg #include "pretty-print.h" 38 1.1 mrg #include "tree-pretty-print.h" 39 1.1 mrg #include "gimple-pretty-print.h" 40 1.1 mrg #include "cgraph.h" 41 1.1 mrg 42 1.1 mrg #include "langhooks.h" 43 1.1 mrg #include "version.h" 44 1.1 mrg #include "context.h" 45 1.1 mrg #include "pass_manager.h" 46 1.1 mrg #include "selftest.h" 47 1.1 mrg #include "dump-context.h" 48 1.1 mrg #include <zlib.h> 49 1.1 mrg 50 1.1 mrg /* optrecord_json_writer's ctor. Populate the top-level parts of the 51 1.1 mrg in-memory JSON representation. */ 52 1.1 mrg 53 1.1 mrg optrecord_json_writer::optrecord_json_writer () 54 1.1 mrg : m_root_tuple (NULL), m_scopes () 55 1.1 mrg { 56 1.1 mrg m_root_tuple = new json::array (); 57 1.1 mrg 58 1.1.1.3 mrg /* Populate with metadata; compare with toplev.cc: print_version. */ 59 1.1 mrg json::object *metadata = new json::object (); 60 1.1 mrg m_root_tuple->append (metadata); 61 1.1 mrg metadata->set ("format", new json::string ("1")); 62 1.1 mrg json::object *generator = new json::object (); 63 1.1 mrg metadata->set ("generator", generator); 64 1.1 mrg generator->set ("name", new json::string (lang_hooks.name)); 65 1.1 mrg generator->set ("pkgversion", new json::string (pkgversion_string)); 66 1.1 mrg generator->set ("version", new json::string (version_string)); 67 1.1 mrg /* TARGET_NAME is passed in by the Makefile. */ 68 1.1 mrg generator->set ("target", new json::string (TARGET_NAME)); 69 1.1 mrg 70 1.1 mrg /* TODO: capture command-line? 71 1.1.1.3 mrg see gen_producer_string in dwarf2out.cc (currently static). */ 72 1.1 mrg 73 1.1 mrg /* TODO: capture "any plugins?" flag (or the plugins themselves). */ 74 1.1 mrg 75 1.1 mrg json::array *passes = new json::array (); 76 1.1 mrg m_root_tuple->append (passes); 77 1.1 mrg 78 1.1 mrg /* Call add_pass_list for all of the pass lists. */ 79 1.1 mrg { 80 1.1 mrg #define DEF_PASS_LIST(LIST) \ 81 1.1 mrg add_pass_list (passes, g->get_passes ()->LIST); 82 1.1 mrg GCC_PASS_LISTS 83 1.1 mrg #undef DEF_PASS_LIST 84 1.1 mrg } 85 1.1 mrg 86 1.1 mrg json::array *records = new json::array (); 87 1.1 mrg m_root_tuple->append (records); 88 1.1 mrg 89 1.1 mrg m_scopes.safe_push (records); 90 1.1 mrg } 91 1.1 mrg 92 1.1 mrg /* optrecord_json_writer's ctor. 93 1.1 mrg Delete the in-memory JSON representation. */ 94 1.1 mrg 95 1.1 mrg optrecord_json_writer::~optrecord_json_writer () 96 1.1 mrg { 97 1.1 mrg delete m_root_tuple; 98 1.1 mrg } 99 1.1 mrg 100 1.1 mrg /* Choose an appropriate filename, and write the saved records to it. */ 101 1.1 mrg 102 1.1 mrg void 103 1.1 mrg optrecord_json_writer::write () const 104 1.1 mrg { 105 1.1 mrg pretty_printer pp; 106 1.1 mrg m_root_tuple->print (&pp); 107 1.1 mrg 108 1.1 mrg bool emitted_error = false; 109 1.1 mrg char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL); 110 1.1 mrg gzFile outfile = gzopen (filename, "w"); 111 1.1 mrg if (outfile == NULL) 112 1.1 mrg { 113 1.1 mrg error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records", 114 1.1 mrg filename); // FIXME: more info? 115 1.1 mrg goto cleanup; 116 1.1 mrg } 117 1.1 mrg 118 1.1 mrg if (gzputs (outfile, pp_formatted_text (&pp)) <= 0) 119 1.1 mrg { 120 1.1 mrg int tmp; 121 1.1 mrg error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s", 122 1.1 mrg filename, gzerror (outfile, &tmp)); 123 1.1 mrg emitted_error = true; 124 1.1 mrg } 125 1.1 mrg 126 1.1 mrg cleanup: 127 1.1 mrg if (outfile) 128 1.1 mrg if (gzclose (outfile) != Z_OK) 129 1.1 mrg if (!emitted_error) 130 1.1 mrg error_at (UNKNOWN_LOCATION, "error closing optimization records %qs", 131 1.1 mrg filename); 132 1.1 mrg 133 1.1 mrg free (filename); 134 1.1 mrg } 135 1.1 mrg 136 1.1 mrg /* Add a record for OPTINFO to the queue of records to be written. */ 137 1.1 mrg 138 1.1 mrg void 139 1.1 mrg optrecord_json_writer::add_record (const optinfo *optinfo) 140 1.1 mrg { 141 1.1 mrg json::object *obj = optinfo_to_json (optinfo); 142 1.1 mrg 143 1.1 mrg add_record (obj); 144 1.1 mrg 145 1.1 mrg /* Potentially push the scope. */ 146 1.1 mrg if (optinfo->get_kind () == OPTINFO_KIND_SCOPE) 147 1.1 mrg { 148 1.1 mrg json::array *children = new json::array (); 149 1.1 mrg obj->set ("children", children); 150 1.1 mrg m_scopes.safe_push (children); 151 1.1 mrg } 152 1.1 mrg } 153 1.1 mrg 154 1.1 mrg /* Private methods of optrecord_json_writer. */ 155 1.1 mrg 156 1.1.1.2 mrg /* Add record OBJ to the innermost scope. */ 157 1.1 mrg 158 1.1 mrg void 159 1.1 mrg optrecord_json_writer::add_record (json::object *obj) 160 1.1 mrg { 161 1.1 mrg /* Add to innermost scope. */ 162 1.1 mrg gcc_assert (m_scopes.length () > 0); 163 1.1 mrg m_scopes[m_scopes.length () - 1]->append (obj); 164 1.1 mrg } 165 1.1 mrg 166 1.1 mrg /* Pop the innermost scope. */ 167 1.1 mrg 168 1.1 mrg void 169 1.1 mrg optrecord_json_writer::pop_scope () 170 1.1 mrg { 171 1.1 mrg m_scopes.pop (); 172 1.1 mrg 173 1.1 mrg /* We should never pop the top-level records array. */ 174 1.1 mrg gcc_assert (m_scopes.length () > 0); 175 1.1 mrg } 176 1.1 mrg 177 1.1 mrg /* Create a JSON object representing LOC. */ 178 1.1 mrg 179 1.1 mrg json::object * 180 1.1 mrg optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc) 181 1.1 mrg { 182 1.1 mrg json::object *obj = new json::object (); 183 1.1 mrg obj->set ("file", new json::string (loc.m_file)); 184 1.1.1.2 mrg obj->set ("line", new json::integer_number (loc.m_line)); 185 1.1 mrg if (loc.m_function) 186 1.1 mrg obj->set ("function", new json::string (loc.m_function)); 187 1.1 mrg return obj; 188 1.1 mrg } 189 1.1 mrg 190 1.1 mrg /* Create a JSON object representing LOC. */ 191 1.1 mrg 192 1.1 mrg json::object * 193 1.1 mrg optrecord_json_writer::location_to_json (location_t loc) 194 1.1 mrg { 195 1.1 mrg gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION); 196 1.1 mrg expanded_location exploc = expand_location (loc); 197 1.1 mrg json::object *obj = new json::object (); 198 1.1 mrg obj->set ("file", new json::string (exploc.file)); 199 1.1.1.2 mrg obj->set ("line", new json::integer_number (exploc.line)); 200 1.1.1.2 mrg obj->set ("column", new json::integer_number (exploc.column)); 201 1.1 mrg return obj; 202 1.1 mrg } 203 1.1 mrg 204 1.1 mrg /* Create a JSON object representing COUNT. */ 205 1.1 mrg 206 1.1 mrg json::object * 207 1.1 mrg optrecord_json_writer::profile_count_to_json (profile_count count) 208 1.1 mrg { 209 1.1 mrg json::object *obj = new json::object (); 210 1.1.1.2 mrg obj->set ("value", new json::integer_number (count.to_gcov_type ())); 211 1.1 mrg obj->set ("quality", 212 1.1 mrg new json::string (profile_quality_as_string (count.quality ()))); 213 1.1 mrg return obj; 214 1.1 mrg } 215 1.1 mrg 216 1.1 mrg /* Get a string for use when referring to PASS in the saved optimization 217 1.1 mrg records. */ 218 1.1 mrg 219 1.1 mrg json::string * 220 1.1 mrg optrecord_json_writer::get_id_value_for_pass (opt_pass *pass) 221 1.1 mrg { 222 1.1 mrg pretty_printer pp; 223 1.1 mrg /* this is host-dependent, but will be consistent for a given host. */ 224 1.1 mrg pp_pointer (&pp, static_cast<void *> (pass)); 225 1.1 mrg return new json::string (pp_formatted_text (&pp)); 226 1.1 mrg } 227 1.1 mrg 228 1.1 mrg /* Create a JSON object representing PASS. */ 229 1.1 mrg 230 1.1 mrg json::object * 231 1.1 mrg optrecord_json_writer::pass_to_json (opt_pass *pass) 232 1.1 mrg { 233 1.1 mrg json::object *obj = new json::object (); 234 1.1 mrg const char *type = NULL; 235 1.1 mrg switch (pass->type) 236 1.1 mrg { 237 1.1 mrg default: 238 1.1 mrg gcc_unreachable (); 239 1.1 mrg case GIMPLE_PASS: 240 1.1 mrg type = "gimple"; 241 1.1 mrg break; 242 1.1 mrg case RTL_PASS: 243 1.1 mrg type = "rtl"; 244 1.1 mrg break; 245 1.1 mrg case SIMPLE_IPA_PASS: 246 1.1 mrg type = "simple_ipa"; 247 1.1 mrg break; 248 1.1 mrg case IPA_PASS: 249 1.1 mrg type = "ipa"; 250 1.1 mrg break; 251 1.1 mrg } 252 1.1 mrg obj->set ("id", get_id_value_for_pass (pass)); 253 1.1 mrg obj->set ("type", new json::string (type)); 254 1.1 mrg obj->set ("name", new json::string (pass->name)); 255 1.1 mrg /* Represent the optgroup flags as an array. */ 256 1.1 mrg { 257 1.1 mrg json::array *optgroups = new json::array (); 258 1.1 mrg obj->set ("optgroups", optgroups); 259 1.1 mrg for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options; 260 1.1 mrg optgroup->name != NULL; optgroup++) 261 1.1 mrg if (optgroup->value != OPTGROUP_ALL 262 1.1 mrg && (pass->optinfo_flags & optgroup->value)) 263 1.1 mrg optgroups->append (new json::string (optgroup->name)); 264 1.1 mrg } 265 1.1.1.2 mrg obj->set ("num", new json::integer_number (pass->static_pass_number)); 266 1.1 mrg return obj; 267 1.1 mrg } 268 1.1 mrg 269 1.1 mrg /* Create a JSON array for LOC representing the chain of inlining 270 1.1 mrg locations. 271 1.1 mrg Compare with lhd_print_error_function and cp_print_error_function. */ 272 1.1 mrg 273 1.1 mrg json::value * 274 1.1 mrg optrecord_json_writer::inlining_chain_to_json (location_t loc) 275 1.1 mrg { 276 1.1 mrg json::array *array = new json::array (); 277 1.1 mrg 278 1.1 mrg tree abstract_origin = LOCATION_BLOCK (loc); 279 1.1 mrg 280 1.1 mrg while (abstract_origin) 281 1.1 mrg { 282 1.1 mrg location_t *locus; 283 1.1 mrg tree block = abstract_origin; 284 1.1 mrg 285 1.1 mrg locus = &BLOCK_SOURCE_LOCATION (block); 286 1.1 mrg tree fndecl = NULL; 287 1.1 mrg block = BLOCK_SUPERCONTEXT (block); 288 1.1 mrg while (block && TREE_CODE (block) == BLOCK 289 1.1 mrg && BLOCK_ABSTRACT_ORIGIN (block)) 290 1.1 mrg { 291 1.1 mrg tree ao = BLOCK_ABSTRACT_ORIGIN (block); 292 1.1 mrg if (TREE_CODE (ao) == FUNCTION_DECL) 293 1.1 mrg { 294 1.1 mrg fndecl = ao; 295 1.1 mrg break; 296 1.1 mrg } 297 1.1 mrg else if (TREE_CODE (ao) != BLOCK) 298 1.1 mrg break; 299 1.1 mrg 300 1.1 mrg block = BLOCK_SUPERCONTEXT (block); 301 1.1 mrg } 302 1.1 mrg if (fndecl) 303 1.1 mrg abstract_origin = block; 304 1.1 mrg else 305 1.1 mrg { 306 1.1 mrg while (block && TREE_CODE (block) == BLOCK) 307 1.1 mrg block = BLOCK_SUPERCONTEXT (block); 308 1.1 mrg 309 1.1 mrg if (block && TREE_CODE (block) == FUNCTION_DECL) 310 1.1 mrg fndecl = block; 311 1.1 mrg abstract_origin = NULL; 312 1.1 mrg } 313 1.1 mrg if (fndecl) 314 1.1 mrg { 315 1.1 mrg json::object *obj = new json::object (); 316 1.1 mrg const char *printable_name 317 1.1 mrg = lang_hooks.decl_printable_name (fndecl, 2); 318 1.1 mrg obj->set ("fndecl", new json::string (printable_name)); 319 1.1 mrg if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION) 320 1.1 mrg obj->set ("site", location_to_json (*locus)); 321 1.1 mrg array->append (obj); 322 1.1 mrg } 323 1.1 mrg } 324 1.1 mrg 325 1.1 mrg return array; 326 1.1 mrg } 327 1.1 mrg 328 1.1 mrg /* Create a JSON object representing OPTINFO. */ 329 1.1 mrg 330 1.1 mrg json::object * 331 1.1 mrg optrecord_json_writer::optinfo_to_json (const optinfo *optinfo) 332 1.1 mrg { 333 1.1 mrg json::object *obj = new json::object (); 334 1.1 mrg 335 1.1 mrg obj->set ("impl_location", 336 1.1 mrg impl_location_to_json (optinfo->get_impl_location ())); 337 1.1 mrg 338 1.1 mrg const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ()); 339 1.1 mrg obj->set ("kind", new json::string (kind_str)); 340 1.1 mrg json::array *message = new json::array (); 341 1.1 mrg obj->set ("message", message); 342 1.1 mrg for (unsigned i = 0; i < optinfo->num_items (); i++) 343 1.1 mrg { 344 1.1 mrg const optinfo_item *item = optinfo->get_item (i); 345 1.1 mrg switch (item->get_kind ()) 346 1.1 mrg { 347 1.1 mrg default: 348 1.1 mrg gcc_unreachable (); 349 1.1 mrg case OPTINFO_ITEM_KIND_TEXT: 350 1.1 mrg { 351 1.1 mrg message->append (new json::string (item->get_text ())); 352 1.1 mrg } 353 1.1 mrg break; 354 1.1 mrg case OPTINFO_ITEM_KIND_TREE: 355 1.1 mrg { 356 1.1 mrg json::object *json_item = new json::object (); 357 1.1 mrg json_item->set ("expr", new json::string (item->get_text ())); 358 1.1 mrg 359 1.1 mrg /* Capture any location for the node. */ 360 1.1 mrg if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION) 361 1.1 mrg json_item->set ("location", 362 1.1 mrg location_to_json (item->get_location ())); 363 1.1 mrg 364 1.1 mrg message->append (json_item); 365 1.1 mrg } 366 1.1 mrg break; 367 1.1 mrg case OPTINFO_ITEM_KIND_GIMPLE: 368 1.1 mrg { 369 1.1 mrg json::object *json_item = new json::object (); 370 1.1 mrg json_item->set ("stmt", new json::string (item->get_text ())); 371 1.1 mrg 372 1.1 mrg /* Capture any location for the stmt. */ 373 1.1 mrg if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION) 374 1.1 mrg json_item->set ("location", 375 1.1 mrg location_to_json (item->get_location ())); 376 1.1 mrg 377 1.1 mrg message->append (json_item); 378 1.1 mrg } 379 1.1 mrg break; 380 1.1 mrg case OPTINFO_ITEM_KIND_SYMTAB_NODE: 381 1.1 mrg { 382 1.1 mrg json::object *json_item = new json::object (); 383 1.1 mrg json_item->set ("symtab_node", new json::string (item->get_text ())); 384 1.1 mrg 385 1.1 mrg /* Capture any location for the node. */ 386 1.1 mrg if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION) 387 1.1 mrg json_item->set ("location", 388 1.1 mrg location_to_json (item->get_location ())); 389 1.1 mrg message->append (json_item); 390 1.1 mrg } 391 1.1 mrg break; 392 1.1 mrg } 393 1.1 mrg } 394 1.1 mrg 395 1.1 mrg if (optinfo->get_pass ()) 396 1.1 mrg obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ())); 397 1.1 mrg 398 1.1 mrg profile_count count = optinfo->get_count (); 399 1.1 mrg if (count.initialized_p ()) 400 1.1 mrg obj->set ("count", profile_count_to_json (count)); 401 1.1 mrg 402 1.1 mrg /* Record any location, handling the case where of an UNKNOWN_LOCATION 403 1.1 mrg within an inlined block. */ 404 1.1 mrg location_t loc = optinfo->get_location_t (); 405 1.1 mrg if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION) 406 1.1 mrg { 407 1.1 mrg // TOOD: record the location (just caret for now) 408 1.1 mrg // TODO: start/finish also? 409 1.1 mrg obj->set ("location", location_to_json (loc)); 410 1.1 mrg } 411 1.1 mrg 412 1.1 mrg if (current_function_decl) 413 1.1 mrg { 414 1.1 mrg const char *fnname 415 1.1 mrg = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); 416 1.1 mrg obj->set ("function", new json::string (fnname)); 417 1.1 mrg } 418 1.1 mrg 419 1.1 mrg if (loc != UNKNOWN_LOCATION) 420 1.1 mrg obj->set ("inlining_chain", inlining_chain_to_json (loc)); 421 1.1 mrg 422 1.1 mrg return obj; 423 1.1 mrg } 424 1.1 mrg 425 1.1 mrg /* Add a json description of PASS and its siblings to ARR, recursing into 426 1.1 mrg child passes (adding their descriptions within a "children" array). */ 427 1.1 mrg 428 1.1 mrg void 429 1.1 mrg optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass) 430 1.1 mrg { 431 1.1 mrg do 432 1.1 mrg { 433 1.1 mrg json::object *pass_obj = pass_to_json (pass); 434 1.1 mrg arr->append (pass_obj); 435 1.1 mrg if (pass->sub) 436 1.1 mrg { 437 1.1 mrg json::array *sub = new json::array (); 438 1.1 mrg pass_obj->set ("children", sub); 439 1.1 mrg add_pass_list (sub, pass->sub); 440 1.1 mrg } 441 1.1 mrg pass = pass->next; 442 1.1 mrg } 443 1.1 mrg while (pass); 444 1.1 mrg } 445 1.1 mrg 446 1.1 mrg #if CHECKING_P 447 1.1 mrg 448 1.1 mrg namespace selftest { 449 1.1 mrg 450 1.1 mrg /* Verify that we can build a JSON optimization record from dump_* 451 1.1 mrg calls. */ 452 1.1 mrg 453 1.1 mrg static void 454 1.1 mrg test_building_json_from_dump_calls () 455 1.1 mrg { 456 1.1 mrg temp_dump_context tmp (true, true, MSG_NOTE); 457 1.1 mrg dump_user_location_t loc; 458 1.1 mrg dump_printf_loc (MSG_NOTE, loc, "test of tree: "); 459 1.1 mrg dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node); 460 1.1 mrg optinfo *info = tmp.get_pending_optinfo (); 461 1.1 mrg ASSERT_TRUE (info != NULL); 462 1.1 mrg ASSERT_EQ (info->num_items (), 2); 463 1.1 mrg 464 1.1 mrg optrecord_json_writer writer; 465 1.1 mrg json::object *json_obj = writer.optinfo_to_json (info); 466 1.1 mrg ASSERT_TRUE (json_obj != NULL); 467 1.1 mrg 468 1.1 mrg /* Verify that the json is sane. */ 469 1.1 mrg pretty_printer pp; 470 1.1 mrg json_obj->print (&pp); 471 1.1 mrg const char *json_str = pp_formatted_text (&pp); 472 1.1 mrg ASSERT_STR_CONTAINS (json_str, "impl_location"); 473 1.1 mrg ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\""); 474 1.1 mrg ASSERT_STR_CONTAINS (json_str, 475 1.1 mrg "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]"); 476 1.1 mrg delete json_obj; 477 1.1 mrg } 478 1.1 mrg 479 1.1 mrg /* Run all of the selftests within this file. */ 480 1.1 mrg 481 1.1 mrg void 482 1.1 mrg optinfo_emit_json_cc_tests () 483 1.1 mrg { 484 1.1 mrg test_building_json_from_dump_calls (); 485 1.1 mrg } 486 1.1 mrg 487 1.1 mrg } // namespace selftest 488 1.1 mrg 489 1.1 mrg #endif /* CHECKING_P */ 490