2018-07-20 17:37:23 +02:00
|
|
|
/* Emit optimization information as JSON files.
|
2022-01-03 10:42:10 +01:00
|
|
|
Copyright (C) 2018-2022 Free Software Foundation, Inc.
|
2018-07-20 17:37:23 +02:00
|
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
|
|
|
|
This file is part of GCC.
|
|
|
|
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free
|
|
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
|
|
version.
|
|
|
|
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with GCC; see the file COPYING3. If not see
|
|
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "coretypes.h"
|
|
|
|
|
|
|
|
#include "backend.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include "gimple.h"
|
|
|
|
#include "diagnostic-core.h"
|
|
|
|
|
|
|
|
#include "profile.h"
|
|
|
|
#include "output.h"
|
|
|
|
#include "tree-pass.h"
|
|
|
|
|
|
|
|
#include "optinfo.h"
|
|
|
|
#include "optinfo-emit-json.h"
|
|
|
|
#include "json.h"
|
|
|
|
#include "pretty-print.h"
|
|
|
|
#include "tree-pretty-print.h"
|
|
|
|
#include "gimple-pretty-print.h"
|
|
|
|
#include "cgraph.h"
|
|
|
|
|
|
|
|
#include "langhooks.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "context.h"
|
|
|
|
#include "pass_manager.h"
|
|
|
|
#include "selftest.h"
|
|
|
|
#include "dump-context.h"
|
2018-11-13 16:52:45 +01:00
|
|
|
#include <zlib.h>
|
2018-07-20 17:37:23 +02:00
|
|
|
|
|
|
|
/* optrecord_json_writer's ctor. Populate the top-level parts of the
|
|
|
|
in-memory JSON representation. */
|
|
|
|
|
|
|
|
optrecord_json_writer::optrecord_json_writer ()
|
|
|
|
: m_root_tuple (NULL), m_scopes ()
|
|
|
|
{
|
|
|
|
m_root_tuple = new json::array ();
|
|
|
|
|
2022-01-14 16:57:02 +01:00
|
|
|
/* Populate with metadata; compare with toplev.cc: print_version. */
|
2018-07-20 17:37:23 +02:00
|
|
|
json::object *metadata = new json::object ();
|
|
|
|
m_root_tuple->append (metadata);
|
|
|
|
metadata->set ("format", new json::string ("1"));
|
|
|
|
json::object *generator = new json::object ();
|
|
|
|
metadata->set ("generator", generator);
|
|
|
|
generator->set ("name", new json::string (lang_hooks.name));
|
|
|
|
generator->set ("pkgversion", new json::string (pkgversion_string));
|
|
|
|
generator->set ("version", new json::string (version_string));
|
|
|
|
/* TARGET_NAME is passed in by the Makefile. */
|
|
|
|
generator->set ("target", new json::string (TARGET_NAME));
|
|
|
|
|
|
|
|
/* TODO: capture command-line?
|
2022-01-14 16:57:02 +01:00
|
|
|
see gen_producer_string in dwarf2out.cc (currently static). */
|
2018-07-20 17:37:23 +02:00
|
|
|
|
|
|
|
/* TODO: capture "any plugins?" flag (or the plugins themselves). */
|
|
|
|
|
|
|
|
json::array *passes = new json::array ();
|
|
|
|
m_root_tuple->append (passes);
|
|
|
|
|
|
|
|
/* Call add_pass_list for all of the pass lists. */
|
|
|
|
{
|
|
|
|
#define DEF_PASS_LIST(LIST) \
|
|
|
|
add_pass_list (passes, g->get_passes ()->LIST);
|
|
|
|
GCC_PASS_LISTS
|
|
|
|
#undef DEF_PASS_LIST
|
|
|
|
}
|
|
|
|
|
|
|
|
json::array *records = new json::array ();
|
|
|
|
m_root_tuple->append (records);
|
|
|
|
|
|
|
|
m_scopes.safe_push (records);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* optrecord_json_writer's ctor.
|
|
|
|
Delete the in-memory JSON representation. */
|
|
|
|
|
|
|
|
optrecord_json_writer::~optrecord_json_writer ()
|
|
|
|
{
|
|
|
|
delete m_root_tuple;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Choose an appropriate filename, and write the saved records to it. */
|
|
|
|
|
|
|
|
void
|
|
|
|
optrecord_json_writer::write () const
|
|
|
|
{
|
2018-11-13 16:52:45 +01:00
|
|
|
pretty_printer pp;
|
|
|
|
m_root_tuple->print (&pp);
|
|
|
|
|
|
|
|
bool emitted_error = false;
|
|
|
|
char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
|
|
|
|
gzFile outfile = gzopen (filename, "w");
|
|
|
|
if (outfile == NULL)
|
|
|
|
{
|
|
|
|
error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
|
|
|
|
filename); // FIXME: more info?
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
|
2018-07-20 17:37:23 +02:00
|
|
|
{
|
2018-11-13 16:52:45 +01:00
|
|
|
int tmp;
|
|
|
|
error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
|
|
|
|
filename, gzerror (outfile, &tmp));
|
|
|
|
emitted_error = true;
|
2018-07-20 17:37:23 +02:00
|
|
|
}
|
2018-11-13 16:52:45 +01:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (outfile)
|
|
|
|
if (gzclose (outfile) != Z_OK)
|
|
|
|
if (!emitted_error)
|
|
|
|
error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
|
|
|
|
filename);
|
|
|
|
|
2018-07-20 17:37:23 +02:00
|
|
|
free (filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a record for OPTINFO to the queue of records to be written. */
|
|
|
|
|
|
|
|
void
|
|
|
|
optrecord_json_writer::add_record (const optinfo *optinfo)
|
|
|
|
{
|
|
|
|
json::object *obj = optinfo_to_json (optinfo);
|
|
|
|
|
|
|
|
add_record (obj);
|
|
|
|
|
|
|
|
/* Potentially push the scope. */
|
|
|
|
if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
|
|
|
|
{
|
|
|
|
json::array *children = new json::array ();
|
|
|
|
obj->set ("children", children);
|
|
|
|
m_scopes.safe_push (children);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Private methods of optrecord_json_writer. */
|
|
|
|
|
2020-03-18 12:56:26 +01:00
|
|
|
/* Add record OBJ to the innermost scope. */
|
2018-07-20 17:37:23 +02:00
|
|
|
|
|
|
|
void
|
|
|
|
optrecord_json_writer::add_record (json::object *obj)
|
|
|
|
{
|
|
|
|
/* Add to innermost scope. */
|
|
|
|
gcc_assert (m_scopes.length () > 0);
|
|
|
|
m_scopes[m_scopes.length () - 1]->append (obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pop the innermost scope. */
|
|
|
|
|
|
|
|
void
|
|
|
|
optrecord_json_writer::pop_scope ()
|
|
|
|
{
|
|
|
|
m_scopes.pop ();
|
Fix -fsave-optimization-record ICE (PR tree-optimization/87025)
PR tree-optimization/87025 reports an ICE within
-fsave-optimization-record's optrecord_json_writer.
The issue is that dump_context::begin_scope creates an optinfo
of kind OPTINFO_KIND_SCOPE, but fails to call
dump_context::end_any_optinfo, so the optinfo for the scope remains
pending.
The JSON writer would normally push a JSON array for the "scope" optinfo
when the latter is emitted. However, if a dump_* call happens that
doesn't flush the "scope" optinfo e.g. dump_printf (as opposed to
dump_printf_loc), that dump_ call is added to the pending optinfo, and
optinfo::handle_dump_file_kind changes the pending optinfo's m_kind
(e.g. to OPTINFO_KIND_NOTE). Hence when the pending optinfo is
eventually emitted, it isn't OPTINFO_KIND_SCOPE anymore, and hence
the JSON writer doesn't create and push a JSON array for it, leading
to dump_context's view of scopes getting out-of-sync with that of
the JSON writer's.
Later, dump_context::end_scope unconditionally tries to pop the JSON scope
array, but no JSON scope array was added, leading to an assertion
failure (or crash).
The fix is to call dump_context::end_any_optinfo immediately after
creating the scope optinfo, so that it is emitted immediately, ensuring
that the JSON writer stays in-sync with the dump_context.
gcc/ChangeLog:
PR tree-optimization/87025
* dumpfile.c (dump_context::begin_scope): Call end_any_optinfo
immediately after creating the scope optinfo.
(selftest::test_pr87025): New function.
(selftest::dumpfile_c_tests): Call it.
* optinfo-emit-json.cc (optrecord_json_writer::pop_scope): Assert
that we're not popping the top-level records array.
* optinfo.cc (optinfo::handle_dump_file_kind): Assert that we're
not changing the kind of a "scope" optinfo.
gcc/testsuite/ChangeLog:
PR tree-optimization/87025
* gcc.dg/pr87025.c: New test.
From-SVN: r266280
2018-11-19 17:42:03 +01:00
|
|
|
|
|
|
|
/* We should never pop the top-level records array. */
|
|
|
|
gcc_assert (m_scopes.length () > 0);
|
2018-07-20 17:37:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON object representing LOC. */
|
|
|
|
|
|
|
|
json::object *
|
|
|
|
optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
|
|
|
|
{
|
|
|
|
json::object *obj = new json::object ();
|
|
|
|
obj->set ("file", new json::string (loc.m_file));
|
2019-10-22 11:58:27 +02:00
|
|
|
obj->set ("line", new json::integer_number (loc.m_line));
|
2018-07-20 17:37:23 +02:00
|
|
|
if (loc.m_function)
|
|
|
|
obj->set ("function", new json::string (loc.m_function));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON object representing LOC. */
|
|
|
|
|
|
|
|
json::object *
|
|
|
|
optrecord_json_writer::location_to_json (location_t loc)
|
|
|
|
{
|
2018-07-24 18:06:58 +02:00
|
|
|
gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
|
|
|
|
expanded_location exploc = expand_location (loc);
|
2018-07-20 17:37:23 +02:00
|
|
|
json::object *obj = new json::object ();
|
2018-07-24 18:06:58 +02:00
|
|
|
obj->set ("file", new json::string (exploc.file));
|
2019-10-22 11:58:27 +02:00
|
|
|
obj->set ("line", new json::integer_number (exploc.line));
|
|
|
|
obj->set ("column", new json::integer_number (exploc.column));
|
2018-07-20 17:37:23 +02:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON object representing COUNT. */
|
|
|
|
|
|
|
|
json::object *
|
|
|
|
optrecord_json_writer::profile_count_to_json (profile_count count)
|
|
|
|
{
|
|
|
|
json::object *obj = new json::object ();
|
2019-10-22 11:58:27 +02:00
|
|
|
obj->set ("value", new json::integer_number (count.to_gcov_type ()));
|
2018-07-20 17:37:23 +02:00
|
|
|
obj->set ("quality",
|
|
|
|
new json::string (profile_quality_as_string (count.quality ())));
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get a string for use when referring to PASS in the saved optimization
|
|
|
|
records. */
|
|
|
|
|
|
|
|
json::string *
|
|
|
|
optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
|
|
|
|
{
|
|
|
|
pretty_printer pp;
|
|
|
|
/* this is host-dependent, but will be consistent for a given host. */
|
|
|
|
pp_pointer (&pp, static_cast<void *> (pass));
|
|
|
|
return new json::string (pp_formatted_text (&pp));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON object representing PASS. */
|
|
|
|
|
|
|
|
json::object *
|
|
|
|
optrecord_json_writer::pass_to_json (opt_pass *pass)
|
|
|
|
{
|
|
|
|
json::object *obj = new json::object ();
|
|
|
|
const char *type = NULL;
|
|
|
|
switch (pass->type)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
gcc_unreachable ();
|
|
|
|
case GIMPLE_PASS:
|
|
|
|
type = "gimple";
|
|
|
|
break;
|
|
|
|
case RTL_PASS:
|
|
|
|
type = "rtl";
|
|
|
|
break;
|
|
|
|
case SIMPLE_IPA_PASS:
|
|
|
|
type = "simple_ipa";
|
|
|
|
break;
|
|
|
|
case IPA_PASS:
|
|
|
|
type = "ipa";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
obj->set ("id", get_id_value_for_pass (pass));
|
|
|
|
obj->set ("type", new json::string (type));
|
|
|
|
obj->set ("name", new json::string (pass->name));
|
|
|
|
/* Represent the optgroup flags as an array. */
|
|
|
|
{
|
|
|
|
json::array *optgroups = new json::array ();
|
|
|
|
obj->set ("optgroups", optgroups);
|
|
|
|
for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
|
|
|
|
optgroup->name != NULL; optgroup++)
|
|
|
|
if (optgroup->value != OPTGROUP_ALL
|
|
|
|
&& (pass->optinfo_flags & optgroup->value))
|
|
|
|
optgroups->append (new json::string (optgroup->name));
|
|
|
|
}
|
2019-10-22 11:58:27 +02:00
|
|
|
obj->set ("num", new json::integer_number (pass->static_pass_number));
|
2018-07-20 17:37:23 +02:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON array for LOC representing the chain of inlining
|
|
|
|
locations.
|
|
|
|
Compare with lhd_print_error_function and cp_print_error_function. */
|
|
|
|
|
|
|
|
json::value *
|
|
|
|
optrecord_json_writer::inlining_chain_to_json (location_t loc)
|
|
|
|
{
|
|
|
|
json::array *array = new json::array ();
|
|
|
|
|
|
|
|
tree abstract_origin = LOCATION_BLOCK (loc);
|
|
|
|
|
|
|
|
while (abstract_origin)
|
|
|
|
{
|
|
|
|
location_t *locus;
|
|
|
|
tree block = abstract_origin;
|
|
|
|
|
|
|
|
locus = &BLOCK_SOURCE_LOCATION (block);
|
|
|
|
tree fndecl = NULL;
|
|
|
|
block = BLOCK_SUPERCONTEXT (block);
|
|
|
|
while (block && TREE_CODE (block) == BLOCK
|
|
|
|
&& BLOCK_ABSTRACT_ORIGIN (block))
|
|
|
|
{
|
|
|
|
tree ao = BLOCK_ABSTRACT_ORIGIN (block);
|
|
|
|
if (TREE_CODE (ao) == FUNCTION_DECL)
|
|
|
|
{
|
|
|
|
fndecl = ao;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (TREE_CODE (ao) != BLOCK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
block = BLOCK_SUPERCONTEXT (block);
|
|
|
|
}
|
|
|
|
if (fndecl)
|
|
|
|
abstract_origin = block;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (block && TREE_CODE (block) == BLOCK)
|
|
|
|
block = BLOCK_SUPERCONTEXT (block);
|
|
|
|
|
|
|
|
if (block && TREE_CODE (block) == FUNCTION_DECL)
|
|
|
|
fndecl = block;
|
|
|
|
abstract_origin = NULL;
|
|
|
|
}
|
|
|
|
if (fndecl)
|
|
|
|
{
|
|
|
|
json::object *obj = new json::object ();
|
|
|
|
const char *printable_name
|
|
|
|
= lang_hooks.decl_printable_name (fndecl, 2);
|
|
|
|
obj->set ("fndecl", new json::string (printable_name));
|
2018-07-24 18:06:58 +02:00
|
|
|
if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
|
2018-07-20 17:37:23 +02:00
|
|
|
obj->set ("site", location_to_json (*locus));
|
|
|
|
array->append (obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a JSON object representing OPTINFO. */
|
|
|
|
|
|
|
|
json::object *
|
|
|
|
optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
|
|
|
|
{
|
|
|
|
json::object *obj = new json::object ();
|
|
|
|
|
|
|
|
obj->set ("impl_location",
|
|
|
|
impl_location_to_json (optinfo->get_impl_location ()));
|
|
|
|
|
|
|
|
const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
|
|
|
|
obj->set ("kind", new json::string (kind_str));
|
|
|
|
json::array *message = new json::array ();
|
|
|
|
obj->set ("message", message);
|
|
|
|
for (unsigned i = 0; i < optinfo->num_items (); i++)
|
|
|
|
{
|
|
|
|
const optinfo_item *item = optinfo->get_item (i);
|
|
|
|
switch (item->get_kind ())
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
gcc_unreachable ();
|
|
|
|
case OPTINFO_ITEM_KIND_TEXT:
|
|
|
|
{
|
|
|
|
message->append (new json::string (item->get_text ()));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OPTINFO_ITEM_KIND_TREE:
|
|
|
|
{
|
|
|
|
json::object *json_item = new json::object ();
|
|
|
|
json_item->set ("expr", new json::string (item->get_text ()));
|
|
|
|
|
|
|
|
/* Capture any location for the node. */
|
2018-07-24 18:06:58 +02:00
|
|
|
if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
|
|
|
|
json_item->set ("location",
|
|
|
|
location_to_json (item->get_location ()));
|
2018-07-20 17:37:23 +02:00
|
|
|
|
|
|
|
message->append (json_item);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OPTINFO_ITEM_KIND_GIMPLE:
|
|
|
|
{
|
|
|
|
json::object *json_item = new json::object ();
|
|
|
|
json_item->set ("stmt", new json::string (item->get_text ()));
|
|
|
|
|
|
|
|
/* Capture any location for the stmt. */
|
2018-07-24 18:06:58 +02:00
|
|
|
if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
|
|
|
|
json_item->set ("location",
|
|
|
|
location_to_json (item->get_location ()));
|
2018-07-20 17:37:23 +02:00
|
|
|
|
|
|
|
message->append (json_item);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OPTINFO_ITEM_KIND_SYMTAB_NODE:
|
|
|
|
{
|
|
|
|
json::object *json_item = new json::object ();
|
|
|
|
json_item->set ("symtab_node", new json::string (item->get_text ()));
|
|
|
|
|
|
|
|
/* Capture any location for the node. */
|
2018-07-24 18:06:58 +02:00
|
|
|
if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
|
|
|
|
json_item->set ("location",
|
|
|
|
location_to_json (item->get_location ()));
|
2018-07-20 17:37:23 +02:00
|
|
|
message->append (json_item);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optinfo->get_pass ())
|
|
|
|
obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
|
|
|
|
|
|
|
|
profile_count count = optinfo->get_count ();
|
|
|
|
if (count.initialized_p ())
|
|
|
|
obj->set ("count", profile_count_to_json (count));
|
|
|
|
|
|
|
|
/* Record any location, handling the case where of an UNKNOWN_LOCATION
|
|
|
|
within an inlined block. */
|
|
|
|
location_t loc = optinfo->get_location_t ();
|
|
|
|
if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
|
|
|
|
{
|
|
|
|
// TOOD: record the location (just caret for now)
|
|
|
|
// TODO: start/finish also?
|
|
|
|
obj->set ("location", location_to_json (loc));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_function_decl)
|
|
|
|
{
|
2019-03-28 15:40:56 +01:00
|
|
|
const char *fnname
|
|
|
|
= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
|
2018-07-20 17:37:23 +02:00
|
|
|
obj->set ("function", new json::string (fnname));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loc != UNKNOWN_LOCATION)
|
|
|
|
obj->set ("inlining_chain", inlining_chain_to_json (loc));
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a json description of PASS and its siblings to ARR, recursing into
|
|
|
|
child passes (adding their descriptions within a "children" array). */
|
|
|
|
|
|
|
|
void
|
|
|
|
optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
json::object *pass_obj = pass_to_json (pass);
|
|
|
|
arr->append (pass_obj);
|
|
|
|
if (pass->sub)
|
|
|
|
{
|
|
|
|
json::array *sub = new json::array ();
|
|
|
|
pass_obj->set ("children", sub);
|
|
|
|
add_pass_list (sub, pass->sub);
|
|
|
|
}
|
|
|
|
pass = pass->next;
|
|
|
|
}
|
|
|
|
while (pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if CHECKING_P
|
|
|
|
|
|
|
|
namespace selftest {
|
|
|
|
|
|
|
|
/* Verify that we can build a JSON optimization record from dump_*
|
|
|
|
calls. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_building_json_from_dump_calls ()
|
|
|
|
{
|
Report vectorization problems via a new opt_problem class
This is v3 of the patch; previous versions were:
v2: https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
v1: https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html
This patch introduces a class opt_problem, along with wrapper
classes for bool (opt_result) and for pointers (e.g. opt_loop_vec_info
for loop_vec_info).
opt_problem instances are created when an optimization problem
is encountered, but only if dump_enabled_p. They are manually
propagated up the callstack, and are manually reported at the
"top level" of an optimization if dumping is enabled, to give the user
a concise summary of the problem *after* the failure is reported.
In particular, the location of the problematic statement is
captured and emitted, rather than just the loop's location.
For example:
no-vfa-vect-102.c:24:3: missed: couldn't vectorize loop
no-vfa-vect-102.c:27:7: missed: statement clobbers memory: __asm__ __volatile__("" : : : "memory");
Changed in v3:
* This version bootstraps and passes regression testing (on
x86_64-pc-linux-gnu).
* added selftests, to exercise the opt_problem machinery
* removed the "bool to opt_result" ctor, so that attempts to
use e.g. return a bool from an opt_result-returning function
will fail at compile time
* use formatted printing within opt_problem ctor to replace the
various dump_printf_loc calls
* dropped i18n
* changed the sense of vect_analyze_data_ref_dependence's return
value (see the ChangeLog)
* add MSG_PRIORITY_REEMITTED, so that -fopt-info can show the
messages, without them messing up the counts in scan-tree-dump-times
in DejaGnu tests
gcc/ChangeLog:
* Makefile.in (OBJS): Add opt-problem.o.
* dump-context.h: Include "selftest.h.
(selftest::temp_dump_context): New forward decl.
(class dump_context): Make friend of class
selftest::temp_dump_context.
(dump_context::dump_loc_immediate): New decl.
(class dump_pretty_printer): Move here from dumpfile.c.
(class temp_dump_context): Move to namespace selftest.
(temp_dump_context::temp_dump_context): Add param
"forcibly_enable_dumping".
(selftest::verify_dumped_text):
(ASSERT_DUMPED_TEXT_EQ): Move here from dumpfile.c.
(selftest::verify_item):
(ASSERT_IS_TEXT): Move here from dumpfile.c.
(ASSERT_IS_TREE): Likewise.
(ASSERT_IS_GIMPLE): Likewise.
* dumpfile.c (dump_context::dump_loc): Move immediate dumping
to...
(dump_context::dump_loc_immediate): ...this new function.
(class dump_pretty_printer): Move to dump-context.h.
(dump_switch_p_1): Don't enable MSG_PRIORITY_REEMITTED.
(opt_info_switch_p_1): Enable MSG_PRIORITY_REEMITTED.
(temp_dump_context::temp_dump_context): Move to "selftest"
namespace. Add param "forcibly_enable_dumping", and use it to
conditionalize the use of m_pp;
(selftest::verify_dumped_text): Make non-static.
(ASSERT_DUMPED_TEXT_EQ): Move to dump-context.h.
(selftest::verify_item): Make non-static.
(ASSERT_IS_TEXT): Move to dump-context.h.
(ASSERT_IS_TREE): Likewise.
(ASSERT_IS_GIMPLE): Likewise.
(selftest::test_capture_of_dump_calls): Pass "true" for new
param of temp_dump_context.
* dumpfile.h (enum dump_flag): Add MSG_PRIORITY_REEMITTED, adding
it to MSG_ALL_PRIORITIES. Update values of TDF_COMPARE_DEBUG and
TDF_COMPARE_DEBUG.
* opt-problem.cc: New file.
* opt-problem.h: New file.
* optinfo-emit-json.cc
(selftest::test_building_json_from_dump_calls): Pass "true" for
new param of temp_dump_context.
* optinfo.cc (optinfo_kind_to_dump_flag): New function.
(optinfo::emit_for_opt_problem): New function.
(optinfo::emit): Clarity which emit_item is used.
* optinfo.h (optinfo::get_dump_location): New accessor.
(optinfo::emit_for_opt_problem): New decl.
(optinfo::emit): Make const.
* selftest-run-tests.c (selftest::run_tests): Call
selftest::opt_problem_cc_tests.
* selftest.h (selftest::opt_problem_cc_tests): New decl.
* tree-data-ref.c (dr_analyze_innermost): Convert return type from
bool to opt_result, converting fprintf messages to
opt_result::failure_at calls. Add "stmt" param for use by the
failure_at calls.
(create_data_ref): Pass "stmt" to the dr_analyze_innermost call.
(runtime_alias_check_p): Convert return type from bool to
opt_result, converting dump_printf calls to
opt_result::failure_at, using the statement DDR_A for their
location.
(find_data_references_in_stmt): Convert return type from bool to
opt_result, converting "return false" to opt_result::failure_at
with a new message.
* tree-data-ref.h: Include "opt-problem.h".
(dr_analyze_innermost): Convert return type from bool to opt_result,
and add a const gimple * param.
(find_data_references_in_stmt): Convert return type from bool to
opt_result.
(runtime_alias_check_p): Likewise.
* tree-predcom.c (find_looparound_phi): Pass "init_stmt" to
dr_analyze_innermost.
* tree-vect-data-refs.c (vect_mark_for_runtime_alias_test):
Convert return type from bool to opt_result, adding a message for
the PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS zero case.
(vect_analyze_data_ref_dependence): Convert return type from bool
to opt_result. Change sense of return type from "false"
effectively meaning "no problems" to "false" meaning a problem,
so that "return false" becomes "return opt_result::success".
Convert "return true" calls to opt_result::failure_at, using
the location of statement A rather than vect_location.
(vect_analyze_data_ref_dependences): Convert return type from bool
to opt_result.
(verify_data_ref_alignment): Likewise, converting dump_printf_loc
calls to opt_result::failure_at, using the stmt location rather
than vect_location.
(vect_verify_datarefs_alignment): Convert return type from bool
to opt_result.
(vect_enhance_data_refs_alignment): Likewise. Split local "stat"
into multiple more-tightly-scoped copies.
(vect_analyze_data_refs_alignment): Convert return type from bool
to opt_result.
(vect_analyze_data_ref_accesses): Likewise, converting a
"return false" to a "return opt_result::failure_at", adding a
new message.
(vect_prune_runtime_alias_test_list): Convert return type from
bool to opt_result, converting dump_printf_loc to
opt_result::failure_at. Add a %G to show the pertinent statement,
and use the stmt's location rather than vect_location.
(vect_find_stmt_data_reference): Convert return type from
bool to opt_result, converting dump_printf_loc to
opt_result::failure_at, using stmt's location.
(vect_analyze_data_refs): Convert return type from bool to
opt_result. Convert "return false" to "return
opt_result::failure_at", adding messages as needed.
* tree-vect-loop.c (vect_determine_vf_for_stmt_1): Convert return
type from bool to opt_result.
(vect_determine_vf_for_stmt): Likewise.
(vect_determine_vectorization_factor): Likewise, converting
dump_printf_loc to opt_result::failure_at, using location of phi
rather than vect_location.
(vect_analyze_loop_form_1): Convert return type from bool to
opt_result, converting dump_printf_loc calls, retaining the use of
vect_location.
(vect_analyze_loop_form): Convert return type from loop_vec_info
to opt_loop_vec_info.
(vect_analyze_loop_operations): Convert return type from bool to
opt_result, converting dump_printf_loc calls, using the location
of phi/stmt rather than vect_location where available. Convert
various "return false" to "return opt_result::failure_at" with
"unsupported phi" messages.
(vect_get_datarefs_in_loop): Convert return type from bool to
opt_result. Add a message for the
PARAM_LOOP_MAX_DATAREFS_FOR_DATADEPS failure.
(vect_analyze_loop_2): Convert return type from bool to
opt_result. Ensure "ok" is set to a opt_result::failure_at before
each "goto again;", adding new messages where needed.
Add "unsupported grouped {store|load}" messages.
(vect_analyze_loop): Convert return type from loop_vec_info to
opt_loop_vec_info.
* tree-vect-slp.c (vect_analyze_slp): Convert return type from
bool to opt_result.
* tree-vect-stmts.c (process_use): Likewise, converting
dump_printf_loc call and using stmt location, rather than
vect_location.
(vect_mark_stmts_to_be_vectorized): Likeise.
(vect_analyze_stmt): Likewise, adding a %G.
(vect_get_vector_types_for_stmt): Convert return type from bool to
opt_result, converting dump_printf_loc calls and using stmt
location, rather than vect_location.
(vect_get_mask_type_for_stmt): Convert return type from tree to
opt_tree, converting dump_printf_loc calls and using stmt location.
* tree-vectorizer.c: Include "opt-problem.h.
(try_vectorize_loop_1): Flag "Analyzing loop at" dump message as
MSG_PRIORITY_INTERNALS. Convert local "loop_vinfo" from
loop_vec_info to opt_loop_vec_info. If if fails, and dumping is
enabled, use it to report at the top level "couldn't vectorize
loop" followed by the problem.
* tree-vectorizer.h (opt_loop_vec_info): New typedef.
(vect_mark_stmts_to_be_vectorized): Convert return type from bool
to opt_result.
(vect_analyze_stmt): Likewise.
(vect_get_vector_types_for_stmt): Likewise.
(tree vect_get_mask_type_for_stmt): Likewise.
(vect_analyze_data_ref_dependences): Likewise.
(vect_enhance_data_refs_alignment): Likewise.
(vect_analyze_data_refs_alignment): Likewise.
(vect_verify_datarefs_alignment): Likewise.
(vect_analyze_data_ref_accesses): Likewise.
(vect_prune_runtime_alias_test_list): Likewise.
(vect_find_stmt_data_reference): Likewise.
(vect_analyze_data_refs): Likewise.
(vect_analyze_loop): Convert return type from loop_vec_info to
opt_loop_vec_info.
(vect_analyze_loop_form): Likewise.
(vect_analyze_slp): Convert return type from bool to opt_result.
gcc/testsuite/ChangeLog:
* gcc.dg/vect/nodump-vect-opt-info-2.c: New test.
* gcc.dg/vect/vect-alias-check-4.c: Add "-fopt-info-vec-all" to
dg-additional-options. Add dg-message and dg-missed directives
to verify that -fopt-info messages are written at the correct
locations.
From-SVN: r264852
2018-10-04 19:50:52 +02:00
|
|
|
temp_dump_context tmp (true, true, MSG_NOTE);
|
Fix missing dump_impl_location_t values, using a new dump_metadata_t
The dump_* API attempts to capture emission location metadata for the
various dump messages, but looking in -fsave-optimization-record shows
that many dump messages are lacking useful impl_location values, instead
having this location within dumpfile.c:
"impl_location": {
"file": "../../src/gcc/dumpfile.c",
"function": "ensure_pending_optinfo",
"line": 1169
},
The problem is that the auto-capturing of dump_impl_location_t is tied to
dump_location_t, and this is tied to the dump_*_loc calls. If a message
comes from a dump_* call without a "_loc" suffix (e.g. dump_printf), the
current code synthesizes the dump_location_t within
dump_context::ensure_pending_optinfo, and thus saves the useless
impl_location seen above.
This patch fixes things by changing the dump_* API so that, rather than
taking a dump_flags_t, they take a new class dump_metadata_t, which is
constructed from a dump_flags_t, but captures the emission location.
Hence e.g.:
dump_printf (MSG_NOTE, "some message\n");
implicitly builds a dump_metadata_t wrapping the MSG_NOTE and the
emission location. If there are several dump_printf calls without
a dump_*_loc call, the emission location within the optinfo is that
of the first dump call within it.
The patch updates selftest::test_capture_of_dump_calls to verify
that the impl location of various dump_* calls is captured. I also
verified things manually.
gcc/ChangeLog:
* dump-context.h (dump_context::dump_loc): Convert 1st param from
dump_flags_t to const dump_metadata_t &. Convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_context::dump_loc_immediate): Convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_context::dump_gimple_stmt): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::void dump_gimple_stmt_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_gimple_expr): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_gimple_expr_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_generic_expr): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_generic_expr_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_printf_va): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_printf_loc_va): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_dec): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_symtab_node): Likewise.
(dump_context::begin_scope): Split out 2nd param into
user and impl locations.
(dump_context::ensure_pending_optinfo): Add metadata param.
(dump_context::begin_next_optinfo): Replace dump_location_t param
with metadata and user location.
* dumpfile.c (dump_context::dump_loc): Convert 1st param from
dump_flags_t to const dump_metadata_t &. Convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_context::dump_loc_immediate): Convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_context::dump_gimple_stmt): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::void dump_gimple_stmt_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_gimple_expr): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_gimple_expr_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_generic_expr): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_generic_expr_loc): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_printf_va): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_printf_loc_va): Likewise; convert
2nd param from const dump_location_t & to
const dump_user_location_t &.
(dump_context::dump_dec): Convert 1st param from
dump_flags_t to const dump_metadata_t &.
(dump_context::dump_symtab_node): Likewise.
(dump_context::begin_scope): Split out 2nd param into
user and impl locations.
(dump_context::ensure_pending_optinfo): Add metadata param.
(dump_context::begin_next_optinfo): Replace dump_location_t param
with metadata and user location.
(dump_gimple_stmt): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_gimple_stmt_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_gimple_expr): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_gimple_expr_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_generic_expr): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_generic_expr_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_printf): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_printf_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_dec): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_symtab_node): Likewise.
(dump_begin_scope): Split out 2nd param into user and impl
locations.
(selftest::assert_impl_location_eq): New function.
(ASSERT_IMPL_LOCATION_EQ): New macro.
(selftest::test_impl_location): Update to use
ASSERT_IMPL_LOCATION_EQ.
(selftest::test_capture_of_dump_calls): Convert "loc" to
dump_user_location_t. Add ASSERT_IMPL_LOCATION_EQ throughout,
verifying line numbers of dump emissions.
* dumpfile.h (class dump_metadata_t): New class.
(dump_printf): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_printf_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_generic_expr_loc): Likewise.
(dump_generic_expr): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_gimple_stmt_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_gimple_stmt): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_gimple_expr_loc): Likewise; convert 2nd param from
const dump_location_t & to const dump_user_location_t &.
(dump_gimple_expr): Convert 1st param from dump_flags_t to
const dump_metadata_t &.
(dump_symtab_node): Likewise.
(dump_dec): Likewise.
(dump_begin_scope): Split out 2nd param into user and impl
locations.
(auto_dump_scope::auto_dump_scope): Split "loc" param into a user
location and impl_location, and capture the impl_location.
(AUTO_DUMP_SCOPE): Rename param from LOC to USER_LOC.
* loop-unroll.c (report_unroll): Update for changes to
dump_printf_loc and dump_printf.
* opt-problem.cc (opt_problem::opt_problem): Update for change to
dump_loc.
* optinfo-emit-json.cc
(selftest::test_building_json_from_dump_calls): Convert "loc" from
dump_location_t to dump_user_location_t.
* optinfo.cc (optinfo::emit_for_opt_problem): Update for change in
dump_loc_immediate.
* profile.c (compute_branch_probabilities): Update for change to
dump_printf_loc.
* selftest.h (ASSERT_STR_CONTAINS_AT): New macro.
* tree-vect-slp.c (vect_print_slp_tree): Update for change to
dump_printf_loc.
From-SVN: r266501
2018-11-27 01:55:28 +01:00
|
|
|
dump_user_location_t loc;
|
2018-07-20 17:37:23 +02:00
|
|
|
dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
|
|
|
|
dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
|
|
|
|
optinfo *info = tmp.get_pending_optinfo ();
|
|
|
|
ASSERT_TRUE (info != NULL);
|
|
|
|
ASSERT_EQ (info->num_items (), 2);
|
|
|
|
|
|
|
|
optrecord_json_writer writer;
|
|
|
|
json::object *json_obj = writer.optinfo_to_json (info);
|
|
|
|
ASSERT_TRUE (json_obj != NULL);
|
|
|
|
|
|
|
|
/* Verify that the json is sane. */
|
|
|
|
pretty_printer pp;
|
|
|
|
json_obj->print (&pp);
|
|
|
|
const char *json_str = pp_formatted_text (&pp);
|
|
|
|
ASSERT_STR_CONTAINS (json_str, "impl_location");
|
|
|
|
ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
|
|
|
|
ASSERT_STR_CONTAINS (json_str,
|
|
|
|
"\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
|
|
|
|
delete json_obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Run all of the selftests within this file. */
|
|
|
|
|
|
|
|
void
|
|
|
|
optinfo_emit_json_cc_tests ()
|
|
|
|
{
|
|
|
|
test_building_json_from_dump_calls ();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace selftest
|
|
|
|
|
|
|
|
#endif /* CHECKING_P */
|