From patchwork Wed Jul 11 11:37:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 942425 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-481338-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="EbfOArE5"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41QbXS0Wrcz9s01 for ; Wed, 11 Jul 2018 20:53:43 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=h2zCPN0onaDP+804HGn+YyLtsNrXg8JxWhZz7O+hCG7k//lWIXt0+ iSSs3FMtUBu41RudlPSCUIF8T6VnQnprfu+EdF92Id3Hx+F6VPmUONIsRnQyC8zU qtpswNNsHWB6TrMmMOBYhgXegRHaULGi5SfNU1rcAuWxzXWNcC0tQE= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=f6hzMzRA5jfgC76JAzZgxI87GUs=; b=EbfOArE5SVMK9f35kDfd kweIWbBqAMyeD5oUX2zWiZu5vjA5dipN98sxE1Xn/vxkz49Ut6f3hyzaSSLeXCBI 9bAe5DHTLi0VwgkB0EUxqtDrc4zKTp6e4B2eK/plTR5BMHAvtYn/+m/aAGS4GEUN ELdhFMu/KcG+IM/b9Y/GBec= Received: (qmail 105113 invoked by alias); 11 Jul 2018 10:53:34 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 105058 invoked by uid 89); 11 Jul 2018 10:53:32 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=remained, consolidate, terminate X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 11 Jul 2018 10:53:26 +0000 Received: from smtp.corp.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 861B75D5F9; Wed, 11 Jul 2018 10:53:25 +0000 (UTC) Received: from c64.redhat.com (ovpn-112-31.phx2.redhat.com [10.3.112.31]) by smtp.corp.redhat.com (Postfix) with ESMTP id DD4A93001A49; Wed, 11 Jul 2018 10:53:23 +0000 (UTC) From: David Malcolm To: Richard Biener Cc: GCC Patches , David Malcolm Subject: [PATCH 1/2] v5: Add "optinfo" framework Date: Wed, 11 Jul 2018 07:37:26 -0400 Message-Id: <1531309047-48505-1-git-send-email-dmalcolm@redhat.com> In-Reply-To: References: X-IsSubscribed: yes Changes relative to v4: * eliminated optinfo subclasses as discussed * eliminated optinfo-internal.h, moving what remained into optinfo.h * added support for dump_gimple_expr_loc and dump_gimple_expr * more selftests This patch implements a way to consolidate dump_* calls into optinfo objects, as enabling work towards being able to write out optimization records to a file (I'm focussing on that destination in this patch kit, rather than diagnostic remarks). The patch adds the support for building optinfo instances from dump_* calls, but leaves implementing any *users* of them to followup patches. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. OK for trunk? gcc/ChangeLog: * Makefile.in (OBJS): Add optinfo.o. * coretypes.h (class symtab_node): New forward decl. (struct cgraph_node): New forward decl. (class varpool_node): New forward decl. * dump-context.h: New file. * dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h", "tree-pass.h". (refresh_dumps_are_enabled): Use optinfo_enabled_p. (set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed. (set_alt_dump_file): Likewise. (dump_context::~dump_context): New dtor. (dump_gimple_stmt): Move implementation to... (dump_context::dump_gimple_stmt): ...this new member function. Add the stmt to any pending optinfo, creating one if need be. (dump_gimple_stmt_loc): Move implementation to... (dump_context::dump_gimple_stmt_loc): ...this new member function. Start a new optinfo and add the stmt to it. (dump_gimple_expr): Move implementation to... (dump_context::dump_gimple_expr): ...this new member function. Add the stmt to any pending optinfo, creating one if need be. (dump_gimple_expr_loc): Move implementation to... (dump_context::dump_gimple_expr_loc): ...this new member function. Start a new optinfo and add the stmt to it. (dump_generic_expr): Move implementation to... (dump_context::dump_generic_expr): ...this new member function. Add the tree to any pending optinfo, creating one if need be. (dump_generic_expr_loc): Move implementation to... (dump_context::dump_generic_expr_loc): ...this new member function. Add the tree to any pending optinfo, creating one if need be. (dump_printf): Move implementation to... (dump_context::dump_printf_va): ...this new member function. Add the text to any pending optinfo, creating one if need be. (dump_printf_loc): Move implementation to... (dump_context::dump_printf_loc_va): ...this new member function. Start a new optinfo and add the stmt to it. (dump_dec): Move implementation to... (dump_context::dump_dec): ...this new member function. Add the value to any pending optinfo, creating one if need be. (dump_context::dump_symtab_node): New member function. (dump_context::get_scope_depth): New member function. (dump_context::begin_scope): New member function. (dump_context::end_scope): New member function. (dump_context::ensure_pending_optinfo): New member function. (dump_context::begin_next_optinfo): New member function. (dump_context::end_any_optinfo): New member function. (dump_context::s_current): New global. (dump_context::s_default): New global. (dump_scope_depth): Delete global. (dumpfile_ensure_any_optinfo_are_flushed): New function. (dump_symtab_node): New function. (get_dump_scope_depth): Reimplement in terms of dump_context. (dump_begin_scope): Likewise. (dump_end_scope): Likewise. (selftest::temp_dump_context::temp_dump_context): New ctor. (selftest::temp_dump_context::~temp_dump_context): New dtor. (selftest::verify_item): New function. (ASSERT_IS_TEXT): New macro. (ASSERT_IS_TREE): New macro. (ASSERT_IS_GIMPLE): New macro. (selftest::test_capture_of_dump_calls): New test. (selftest::dumpfile_c_tests): Call it. * dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block) (dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc) (dump_gimple_stmt, dump_dec): Gather these related decls and add a descriptive comment. (dump_function, print_combine_total_stats, enable_rtl_dump_file) (dump_node, dump_bb): Move these unrelated decls. (class dump_manager): Add leading comment. * optinfo.cc: New file. * optinfo.h: New file. --- gcc/Makefile.in | 1 + gcc/coretypes.h | 7 + gcc/dump-context.h | 138 +++++++++++++ gcc/dumpfile.c | 597 +++++++++++++++++++++++++++++++++++++++++++++++++---- gcc/dumpfile.h | 84 +++++--- gcc/optinfo.cc | 236 +++++++++++++++++++++ gcc/optinfo.h | 203 ++++++++++++++++++ 7 files changed, 1200 insertions(+), 66 deletions(-) create mode 100644 gcc/dump-context.h create mode 100644 gcc/optinfo.cc create mode 100644 gcc/optinfo.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2a05a66..dd1dfc1 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1427,6 +1427,7 @@ OBJS = \ optabs-libfuncs.o \ optabs-query.o \ optabs-tree.o \ + optinfo.o \ options-save.o \ opts-global.o \ passes.o \ diff --git a/gcc/coretypes.h b/gcc/coretypes.h index 283b4eb..ed0e825 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -134,6 +134,13 @@ struct gomp_single; struct gomp_target; struct gomp_teams; +/* Subclasses of symtab_node, using indentation to show the class + hierarchy. */ + +class symtab_node; + struct cgraph_node; + class varpool_node; + union section; typedef union section section; struct gcc_options; diff --git a/gcc/dump-context.h b/gcc/dump-context.h new file mode 100644 index 0000000..a191e3a --- /dev/null +++ b/gcc/dump-context.h @@ -0,0 +1,138 @@ +/* Support code for handling the various dump_* calls in dumpfile.h + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + + +#ifndef GCC_DUMP_CONTEXT_H +#define GCC_DUMP_CONTEXT_H 1 + +/* A class for handling the various dump_* calls. + + In particular, this class has responsibility for consolidating + the "dump_*" calls into optinfo instances (delimited by "dump_*_loc" + calls), and emitting them. + + Putting this in a class (rather than as global state) allows + for selftesting of this code. */ + +class dump_context +{ + friend class temp_dump_context; + public: + static dump_context &get () { return *s_current; } + + ~dump_context (); + + void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, + gimple *gs, int spc); + + void dump_gimple_stmt_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + gimple *gs, int spc); + + void dump_gimple_expr (dump_flags_t dump_kind, + dump_flags_t extra_dump_flags, + gimple *gs, int spc); + + void dump_gimple_expr_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + gimple *gs, + int spc); + + void dump_generic_expr (dump_flags_t dump_kind, + dump_flags_t extra_dump_flags, + tree t); + + void dump_generic_expr_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + tree t); + + void dump_printf_va (dump_flags_t dump_kind, const char *format, + va_list ap) ATTRIBUTE_PRINTF (3, 0); + + void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc, + const char *format, va_list ap) + ATTRIBUTE_PRINTF (4, 0); + + template + void dump_dec (dump_flags_t dump_kind, const poly_int &value); + + void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node); + + /* Managing nested scopes. */ + unsigned int get_scope_depth () const; + void begin_scope (const char *name, const dump_location_t &loc); + void end_scope (); + + /* For use in selftests; if true then optinfo_enabled_p is true. */ + bool forcibly_enable_optinfo_p () const + { + return m_forcibly_enable_optinfo; + } + + void end_any_optinfo (); + + private: + optinfo &ensure_pending_optinfo (); + optinfo &begin_next_optinfo (const dump_location_t &loc); + + /* For use in selftests; if true then optinfo_enabled_p is true. */ + bool m_forcibly_enable_optinfo; + + /* The current nesting depth of dump scopes, for showing nesting + via indentation). */ + unsigned int m_scope_depth; + + /* The optinfo currently being accumulated since the last dump_*_loc call, + if any. */ + optinfo *m_pending; + + /* The currently active dump_context, for use by the dump_* API calls. */ + static dump_context *s_current; + + /* The default active context. */ + static dump_context s_default; +}; + +#if CHECKING_P + +/* An RAII-style class for use in selftests for temporarily using a different + dump_context. */ + +class temp_dump_context +{ + public: + temp_dump_context (bool forcibly_enable_optinfo); + ~temp_dump_context (); + + /* Support for selftests. */ + optinfo *get_pending_optinfo () const { return m_context.m_pending; } + + private: + dump_context m_context; + dump_context *m_saved; + bool m_saved_flag_remarks; +}; + +#endif /* CHECKING_P */ + +#endif /* GCC_DUMP_CONTEXT_H */ diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c index 7ed1796..38f9539 100644 --- a/gcc/dumpfile.c +++ b/gcc/dumpfile.c @@ -33,6 +33,10 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" /* for dump_user_location_t ctor. */ #include "rtl.h" /* for dump_user_location_t ctor. */ #include "selftest.h" +#include "optinfo.h" +#include "dump-context.h" +#include "cgraph.h" +#include "tree-pass.h" /* for "current_pass". */ /* If non-NULL, return one past-the-end of the matching SUBPART of the WHOLE string. */ @@ -64,7 +68,7 @@ bool dumps_are_enabled = false; static void refresh_dumps_are_enabled () { - dumps_are_enabled = (dump_file || alt_dump_file); + dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ()); } /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled" @@ -73,6 +77,7 @@ refresh_dumps_are_enabled () void set_dump_file (FILE *new_dump_file) { + dumpfile_ensure_any_optinfo_are_flushed (); dump_file = new_dump_file; refresh_dumps_are_enabled (); } @@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file) static void set_alt_dump_file (FILE *new_alt_dump_file) { + dumpfile_ensure_any_optinfo_are_flushed (); alt_dump_file = new_alt_dump_file; refresh_dumps_are_enabled (); } @@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc) } } +/* Implementation of dump_context member functions. */ + +/* dump_context's dtor. */ + +dump_context::~dump_context () +{ + delete m_pending; +} + /* Dump gimple statement GS with SPC indentation spaces and EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled. */ void -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, - gimple *gs, int spc) +dump_context::dump_gimple_stmt (dump_flags_t dump_kind, + dump_flags_t extra_dump_flags, + gimple *gs, int spc) { if (dump_file && (dump_kind & pflags)) print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags); if (alt_dump_file && (dump_kind & alt_flags)) print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + info.handle_dump_file_kind (dump_kind); + info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags); + } } /* Similar to dump_gimple_stmt, except additionally print source location. */ void -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc, - dump_flags_t extra_dump_flags, gimple *gs, int spc) +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + gimple *gs, int spc) { location_t srcloc = loc.get_location_t (); if (dump_file && (dump_kind & pflags)) @@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc, dump_loc (dump_kind, alt_dump_file, srcloc); print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); } + + if (optinfo_enabled_p ()) + { + optinfo &info = begin_next_optinfo (loc); + info.handle_dump_file_kind (dump_kind); + info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags); + } } /* Dump gimple statement GS with SPC indentation spaces and @@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc, Do not terminate with a newline or semicolon. */ void -dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, - gimple *gs, int spc) +dump_context::dump_gimple_expr (dump_flags_t dump_kind, + dump_flags_t extra_dump_flags, + gimple *gs, int spc) { if (dump_file && (dump_kind & pflags)) print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags); if (alt_dump_file && (dump_kind & alt_flags)) print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + info.handle_dump_file_kind (dump_kind); + info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags); + } } /* Similar to dump_gimple_expr, except additionally print source location. */ void -dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, - dump_flags_t extra_dump_flags, gimple *gs, int spc) +dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + gimple *gs, + int spc) { location_t srcloc = loc.get_location_t (); if (dump_file && (dump_kind & pflags)) @@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, dump_loc (dump_kind, alt_dump_file, srcloc); print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags); } + + if (optinfo_enabled_p ()) + { + optinfo &info = begin_next_optinfo (loc); + info.handle_dump_file_kind (dump_kind); + info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags); + } } @@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, DUMP_KIND is enabled. */ void -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, - tree t) +dump_context::dump_generic_expr (dump_flags_t dump_kind, + dump_flags_t extra_dump_flags, + tree t) { if (dump_file && (dump_kind & pflags)) print_generic_expr (dump_file, t, dump_flags | extra_dump_flags); if (alt_dump_file && (dump_kind & alt_flags)) print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags); + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + info.handle_dump_file_kind (dump_kind); + info.add_tree (t, dump_flags | extra_dump_flags); + } } @@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, location. */ void -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, - dump_flags_t extra_dump_flags, tree t) +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind, + const dump_location_t &loc, + dump_flags_t extra_dump_flags, + tree t) { location_t srcloc = loc.get_location_t (); if (dump_file && (dump_kind & pflags)) @@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, dump_loc (dump_kind, alt_dump_file, srcloc); print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags); } + + if (optinfo_enabled_p ()) + { + optinfo &info = begin_next_optinfo (loc); + info.handle_dump_file_kind (dump_kind); + info.add_tree (t, dump_flags | extra_dump_flags); + } } /* Output a formatted message using FORMAT on appropriate dump streams. */ void -dump_printf (dump_flags_t dump_kind, const char *format, ...) +dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format, + va_list ap) { if (dump_file && (dump_kind & pflags)) { - va_list ap; - va_start (ap, format); - vfprintf (dump_file, format, ap); - va_end (ap); + va_list aq; + va_copy (aq, ap); + vfprintf (dump_file, format, aq); + va_end (aq); } if (alt_dump_file && (dump_kind & alt_flags)) { - va_list ap; - va_start (ap, format); - vfprintf (alt_dump_file, format, ap); - va_end (ap); + va_list aq; + va_copy (aq, ap); + vfprintf (alt_dump_file, format, aq); + va_end (aq); + } + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + va_list aq; + va_copy (aq, ap); + info.add_printf_va (format, aq); + va_end (aq); } } -/* Similar to dump_printf, except source location is also printed. */ +/* Similar to dump_printf, except source location is also printed, and + dump location captured. */ void -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc, - const char *format, ...) +dump_context::dump_printf_loc_va (dump_flags_t dump_kind, + const dump_location_t &loc, + const char *format, va_list ap) { location_t srcloc = loc.get_location_t (); + if (dump_file && (dump_kind & pflags)) { - va_list ap; dump_loc (dump_kind, dump_file, srcloc); - va_start (ap, format); - vfprintf (dump_file, format, ap); - va_end (ap); + va_list aq; + va_copy (aq, ap); + vfprintf (dump_file, format, aq); + va_end (aq); } if (alt_dump_file && (dump_kind & alt_flags)) { - va_list ap; dump_loc (dump_kind, alt_dump_file, srcloc); - va_start (ap, format); - vfprintf (alt_dump_file, format, ap); - va_end (ap); + va_list aq; + va_copy (aq, ap); + vfprintf (alt_dump_file, format, aq); + va_end (aq); + } + + if (optinfo_enabled_p ()) + { + optinfo &info = begin_next_optinfo (loc); + info.handle_dump_file_kind (dump_kind); + va_list aq; + va_copy (aq, ap); + info.add_printf_va (format, aq); + va_end (aq); } } @@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc, template void -dump_dec (dump_flags_t dump_kind, const poly_int &value) +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int &value) { STATIC_ASSERT (poly_coeff_traits::signedness >= 0); signop sgn = poly_coeff_traits::signedness ? SIGNED : UNSIGNED; @@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const poly_int &value) if (alt_dump_file && (dump_kind & alt_flags)) print_dec (value, alt_dump_file, sgn); + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + info.handle_dump_file_kind (dump_kind); + info.add_poly_int (value); + } +} + +/* Output the name of NODE on appropriate dump streams. */ + +void +dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node) +{ + if (dump_file && (dump_kind & pflags)) + fprintf (dump_file, "%s", node->dump_name ()); + + if (alt_dump_file && (dump_kind & alt_flags)) + fprintf (alt_dump_file, "%s", node->dump_name ()); + + if (optinfo_enabled_p ()) + { + optinfo &info = ensure_pending_optinfo (); + info.handle_dump_file_kind (dump_kind); + info.add_symtab_node (node); + } +} + +/* Get the current dump scope-nesting depth. + For use by -fopt-info (for showing nesting via indentation). */ + +unsigned int +dump_context::get_scope_depth () const +{ + return m_scope_depth; +} + +/* Push a nested dump scope. + Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info + destination, if any. + Emit a "scope" optinfo if optinfos are enabled. + Increment the scope depth. */ + +void +dump_context::begin_scope (const char *name, const dump_location_t &loc) +{ + /* Specialcase, to avoid going through dump_printf_loc, + so that we can create a optinfo of kind OPTINFO_KIND_SCOPE. */ + + if (dump_file) + { + dump_loc (MSG_NOTE, dump_file, loc.get_location_t ()); + fprintf (dump_file, "=== %s ===\n", name); + } + + if (alt_dump_file) + { + dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ()); + fprintf (alt_dump_file, "=== %s ===\n", name); + } + + if (optinfo_enabled_p ()) + { + end_any_optinfo (); + optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass); + info.add_printf ("=== %s ===", name); + info.emit (); + } + + m_scope_depth++; +} + +/* Pop a nested dump scope. */ + +void +dump_context::end_scope () +{ + end_any_optinfo (); + m_scope_depth--; +} + +/* Return the optinfo currently being accumulated, creating one if + necessary. */ + +optinfo & +dump_context::ensure_pending_optinfo () +{ + if (!m_pending) + return begin_next_optinfo (dump_location_t (dump_user_location_t ())); + return *m_pending; +} + +/* Start a new optinfo and return it, ending any optinfo that was already + accumulated. */ + +optinfo & +dump_context::begin_next_optinfo (const dump_location_t &loc) +{ + end_any_optinfo (); + gcc_assert (m_pending == NULL); + m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass); + return *m_pending; +} + +/* End any optinfo that has been accumulated within this context; emitting + it to any destinations as appropriate - though none have currently been + implemented. */ + +void +dump_context::end_any_optinfo () +{ + if (m_pending) + m_pending->emit (); + delete m_pending; + m_pending = NULL; +} + +/* The current singleton dump_context, and its default. */ + +dump_context *dump_context::s_current = &dump_context::s_default; +dump_context dump_context::s_default; + +/* Implementation of dump_* API calls, calling into dump_context + member functions. */ + +/* Dump gimple statement GS with SPC indentation spaces and + EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled. */ + +void +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, + gimple *gs, int spc) +{ + dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc); +} + +/* Similar to dump_gimple_stmt, except additionally print source location. */ + +void +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc, + dump_flags_t extra_dump_flags, gimple *gs, int spc) +{ + dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags, + gs, spc); +} + +/* Dump gimple statement GS with SPC indentation spaces and + EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled. + Do not terminate with a newline or semicolon. */ + +void +dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, + gimple *gs, int spc) +{ + dump_context::get ().dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc); +} + +/* Similar to dump_gimple_expr, except additionally print source location. */ + +void +dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, + dump_flags_t extra_dump_flags, gimple *gs, int spc) +{ + dump_context::get ().dump_gimple_expr_loc (dump_kind, loc, extra_dump_flags, + gs, spc); +} + +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if + DUMP_KIND is enabled. */ + +void +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags, + tree t) +{ + dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t); +} + +/* Similar to dump_generic_expr, except additionally print the source + location. */ + +void +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc, + dump_flags_t extra_dump_flags, tree t) +{ + dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags, + t); +} + +/* Output a formatted message using FORMAT on appropriate dump streams. */ + +void +dump_printf (dump_flags_t dump_kind, const char *format, ...) +{ + va_list ap; + va_start (ap, format); + dump_context::get ().dump_printf_va (dump_kind, format, ap); + va_end (ap); +} + +/* Similar to dump_printf, except source location is also printed, and + dump location captured. */ + +void +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc, + const char *format, ...) +{ + va_list ap; + va_start (ap, format); + dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap); + va_end (ap); +} + +/* Output VALUE in decimal to appropriate dump streams. */ + +template +void +dump_dec (dump_flags_t dump_kind, const poly_int &value) +{ + dump_context::get ().dump_dec (dump_kind, value); } template void dump_dec (dump_flags_t, const poly_uint16 &); @@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const poly_wide_int &value) print_hex (value, alt_dump_file); } -/* The current dump scope-nesting depth. */ +/* Emit and delete the currently pending optinfo, if there is one, + without the caller needing to know about class dump_context. */ + +void +dumpfile_ensure_any_optinfo_are_flushed () +{ + dump_context::get().end_any_optinfo (); +} + +/* Output the name of NODE on appropriate dump streams. */ -static int dump_scope_depth; +void +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node) +{ + dump_context::get ().dump_symtab_node (dump_kind, node); +} /* Get the current dump scope-nesting depth. - For use by dump_*_loc (for showing nesting via indentation). */ + For use by -fopt-info (for showing nesting via indentation). */ unsigned int get_dump_scope_depth () { - return dump_scope_depth; + return dump_context::get ().get_scope_depth (); } /* Push a nested dump scope. Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info destination, if any. + Emit a "scope" opinfo if optinfos are enabled. Increment the scope depth. */ void dump_begin_scope (const char *name, const dump_location_t &loc) { - dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name); - dump_scope_depth++; + dump_context::get ().begin_scope (name, loc); } /* Pop a nested dump scope. */ @@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc) void dump_end_scope () { - dump_scope_depth--; + dump_context::get ().end_scope (); } /* Start a dump for PHASE. Store user-supplied dump flags in @@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void) #if CHECKING_P +/* temp_dump_context's ctor. Temporarily override the dump_context + (to forcibly enable optinfo-generation). */ + +temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo) +: m_context (), + m_saved (&dump_context ().get ()) +{ + dump_context::s_current = &m_context; + m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo; +} + +/* temp_dump_context's dtor. Restore the saved dump_context. */ + +temp_dump_context::~temp_dump_context () +{ + dump_context::s_current = m_saved; +} + namespace selftest { /* Verify that the dump_location_t constructors capture the source location @@ -1274,12 +1613,188 @@ test_impl_location () #endif } +/* Verify that ITEM has the expected values. */ + +static void +verify_item (const location &loc, + const optinfo_item *item, + enum optinfo_item_kind expected_kind, + location_t expected_location, + const char *expected_text) +{ + ASSERT_EQ_AT (loc, item->get_kind (), expected_kind); + ASSERT_EQ_AT (loc, item->get_location (), expected_location); + ASSERT_STREQ_AT (loc, item->get_text (), expected_text); +} + +/* Verify that ITEM is a text item, with EXPECTED_TEXT. */ + +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \ + SELFTEST_BEGIN_STMT \ + verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \ + UNKNOWN_LOCATION, (EXPECTED_TEXT)); \ + SELFTEST_END_STMT + +/* Verify that ITEM is a tree item, with the expected values. */ + +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \ + SELFTEST_BEGIN_STMT \ + verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \ + (EXPECTED_LOCATION), (EXPECTED_TEXT)); \ + SELFTEST_END_STMT + +/* Verify that ITEM is a gimple item, with the expected values. */ + +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \ + SELFTEST_BEGIN_STMT \ + verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \ + (EXPECTED_LOCATION), (EXPECTED_TEXT)); \ + SELFTEST_END_STMT + +/* Verify that calls to the dump_* API are captured and consolidated into + optimization records. */ + +static void +test_capture_of_dump_calls (const line_table_case &case_) +{ + /* Generate a location_t for testing. */ + line_table_test ltt (case_); + linemap_add (line_table, LC_ENTER, false, "test.txt", 0); + linemap_line_start (line_table, 5, 100); + linemap_add (line_table, LC_LEAVE, false, NULL, 0); + location_t where = linemap_position_for_column (line_table, 10); + + dump_location_t loc = dump_location_t::from_location_t (where); + + /* Test of dump_printf. */ + { + temp_dump_context tmp (true); + dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo"); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo"); + } + + /* Tree, via dump_generic_expr. */ + { + temp_dump_context tmp (true); + 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->get_location_t (), where); + ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE); + ASSERT_EQ (info->num_items (), 2); + ASSERT_IS_TEXT (info->get_item (0), "test of tree: "); + ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0"); + } + + /* Tree, via dump_generic_expr_loc. */ + { + temp_dump_context tmp (true); + dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->get_location_t (), where); + ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1"); + } + + /* Gimple. */ + { + greturn *stmt = gimple_build_return (NULL); + gimple_set_location (stmt, where); + + /* dump_gimple_stmt_loc. */ + { + temp_dump_context tmp (true); + dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n"); + } + + /* dump_gimple_stmt. */ + { + temp_dump_context tmp (true); + dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n"); + } + + /* dump_gimple_expr_loc. */ + { + temp_dump_context tmp (true); + dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_GIMPLE (info->get_item (0), where, "return;"); + } + + /* dump_gimple_expr. */ + { + temp_dump_context tmp (true); + dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_GIMPLE (info->get_item (0), where, "return;"); + } + } + + /* poly_int. */ + { + temp_dump_context tmp (true); + dump_dec (MSG_NOTE, poly_int64 (42)); + + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_TRUE (info != NULL); + ASSERT_EQ (info->num_items (), 1); + ASSERT_IS_TEXT (info->get_item (0), "42"); + } + + /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE + above. */ + { + /* MSG_OPTIMIZED_LOCATIONS. */ + { + temp_dump_context tmp (true); + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test"); + ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (), + OPTINFO_KIND_SUCCESS); + } + + /* MSG_MISSED_OPTIMIZATION. */ + { + temp_dump_context tmp (true); + dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test"); + ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (), + OPTINFO_KIND_FAILURE); + } + } +} + /* Run all of the selftests within this file. */ void dumpfile_c_tests () { test_impl_location (); + for_each_line_table_case (test_capture_of_dump_calls); } } // namespace selftest diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h index 4a71ef7..3096a89 100644 --- a/gcc/dumpfile.h +++ b/gcc/dumpfile.h @@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *); extern void dump_end (int, FILE *); extern int opt_info_switch_p (const char *); extern const char *dump_flag_name (int); + +/* Global variables used to communicate with passes. */ +extern FILE *dump_file; +extern dump_flags_t dump_flags; +extern const char *dump_file_name; + +extern bool dumps_are_enabled; + +extern void set_dump_file (FILE *new_dump_file); + +/* Return true if any of the dumps is enabled, false otherwise. */ +static inline bool +dump_enabled_p (void) +{ + return dumps_are_enabled; +} + +/* The following API calls (which *don't* take a "FILE *") + write the output to zero or more locations: + (a) the active dump_file, if any + (b) the -fopt-info destination, if any + (c) to the "optinfo" destinations, if any: + + dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file + | + +--> (b) alt_dump_file + | + `--> (c) optinfo + `---> optinfo destinations + + For optinfos, the dump_*_loc mark the beginning of an optinfo + instance: all subsequent dump_* calls are consolidated into + that optinfo, until the next dump_*_loc call (or a change in + dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed). + + A group of dump_* calls should be guarded by: + + if (dump_enabled_p ()) + + to minimize the work done for the common case where dumps + are disabled. */ + extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2; extern void dump_printf_loc (dump_flags_t, const dump_location_t &, const char *, ...) ATTRIBUTE_PRINTF_3; @@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int); extern void dump_gimple_expr_loc (dump_flags_t, const dump_location_t &, dump_flags_t, gimple *, int); extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *, int); -extern void print_combine_total_stats (void); -extern bool enable_rtl_dump_file (void); +extern void dump_symtab_node (dump_flags_t, symtab_node *); template void dump_dec (dump_flags_t, const poly_int &); extern void dump_dec (dump_flags_t, const poly_wide_int &, signop); extern void dump_hex (dump_flags_t, const poly_wide_int &); -/* In tree-dump.c */ -extern void dump_node (const_tree, dump_flags_t, FILE *); - -/* In combine.c */ -extern void dump_combine_total_stats (FILE *); -/* In cfghooks.c */ -extern void dump_bb (FILE *, basic_block, int, dump_flags_t); - -/* Global variables used to communicate with passes. */ -extern FILE *dump_file; -extern dump_flags_t dump_flags; -extern const char *dump_file_name; - -extern bool dumps_are_enabled; - -extern void set_dump_file (FILE *new_dump_file); - -/* Return true if any of the dumps is enabled, false otherwise. */ -static inline bool -dump_enabled_p (void) -{ - return dumps_are_enabled; -} +extern void dumpfile_ensure_any_optinfo_are_flushed (); /* Managing nested scopes, so that dumps can express the call chain leading to a dump message. */ @@ -505,8 +524,23 @@ class auto_dump_scope #define AUTO_DUMP_SCOPE(NAME, LOC) \ auto_dump_scope scope (NAME, LOC) +extern void dump_function (int phase, tree fn); +extern void print_combine_total_stats (void); +extern bool enable_rtl_dump_file (void); + +/* In tree-dump.c */ +extern void dump_node (const_tree, dump_flags_t, FILE *); + +/* In combine.c */ +extern void dump_combine_total_stats (FILE *); +/* In cfghooks.c */ +extern void dump_bb (FILE *, basic_block, int, dump_flags_t); + namespace gcc { +/* A class for managing all of the various dump files used by the + optimization passes. */ + class dump_manager { public: diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc new file mode 100644 index 0000000..6f224bc --- /dev/null +++ b/gcc/optinfo.cc @@ -0,0 +1,236 @@ +/* Optimization information. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "backend.h" +#include "tree.h" +#include "gimple.h" + +#include "optinfo.h" +#include "dump-context.h" +#include "pretty-print.h" +#include "gimple-pretty-print.h" +#include "cgraph.h" +#include "selftest.h" + +/* optinfo_item's ctor. */ + +optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location, + char *text, bool owned) +: m_kind (kind), m_location (location), m_text (text), m_owned (owned) +{ +} + +/* optinfo_item's dtor. */ + +optinfo_item::~optinfo_item () +{ + if (m_owned) + free (m_text); +} + +/* Get a string from KIND. */ + +const char * +optinfo_kind_to_string (enum optinfo_kind kind) +{ + switch (kind) + { + default: + gcc_unreachable (); + case OPTINFO_KIND_SUCCESS: + return "success"; + case OPTINFO_KIND_FAILURE: + return "failure"; + case OPTINFO_KIND_NOTE: + return "note"; + case OPTINFO_KIND_SCOPE: + return "scope"; + } +} + +/* optinfo's dtor. */ + +optinfo::~optinfo () +{ + /* Cleanup. */ + unsigned i; + optinfo_item *item; + FOR_EACH_VEC_ELT (m_items, i, item) + delete item; +} + +/* Emit the optinfo to all of the active destinations. */ + +void +optinfo::emit () +{ + /* currently this is a no-op. */ +} + +/* Update the optinfo's kind based on DUMP_KIND. */ + +void +optinfo::handle_dump_file_kind (dump_flags_t dump_kind) +{ + if (dump_kind & MSG_OPTIMIZED_LOCATIONS) + m_kind = OPTINFO_KIND_SUCCESS; + else if (dump_kind & MSG_MISSED_OPTIMIZATION) + m_kind = OPTINFO_KIND_FAILURE; + else if (dump_kind & MSG_NOTE) + m_kind = OPTINFO_KIND_NOTE; +} + +/* Append a string literal to this optinfo. */ + +void +optinfo::add_string (const char *str) +{ + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + const_cast (str), false); + m_items.safe_push (item); +} + +/* Append printf-formatted text to this optinfo. */ + +void +optinfo::add_printf (const char *format, ...) +{ + va_list ap; + va_start (ap, format); + add_printf_va (format, ap); + va_end (ap); +} + +/* Append printf-formatted text to this optinfo. */ + +void +optinfo::add_printf_va (const char *format, va_list ap) +{ + char *formatted_text = xvasprintf (format, ap); + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + formatted_text, true); + m_items.safe_push (item); +} + +/* Append a gimple statement to this optinfo, equivalent to + print_gimple_stmt. */ + +void +optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags) +{ + pretty_printer pp; + pp_needs_newline (&pp) = true; + pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags); + pp_newline (&pp); + + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt), + xstrdup (pp_formatted_text (&pp)), true); + m_items.safe_push (item); +} + +/* Append a gimple statement to this optinfo, equivalent to + print_gimple_expr. */ + +void +optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags) +{ + dump_flags |= TDF_RHS_ONLY; + pretty_printer pp; + pp_needs_newline (&pp) = true; + pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags); + + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt), + xstrdup (pp_formatted_text (&pp)), true); + m_items.safe_push (item); +} + +/* Append a tree node to this optinfo, equivalent to print_generic_expr. */ + +void +optinfo::add_tree (tree node, dump_flags_t dump_flags) +{ + pretty_printer pp; + pp_needs_newline (&pp) = true; + pp_translate_identifiers (&pp) = false; + dump_generic_node (&pp, node, 0, dump_flags, false); + + location_t loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (node)) + loc = EXPR_LOCATION (node); + + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc, + xstrdup (pp_formatted_text (&pp)), true); + m_items.safe_push (item); +} + +/* Append a symbol table node to this optinfo. */ + +void +optinfo::add_symtab_node (symtab_node *node) +{ + location_t loc = DECL_SOURCE_LOCATION (node->decl); + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc, + xstrdup (node->dump_name ()), true); + m_items.safe_push (item); +} + +/* Append the decimal represenation of a wide_int_ref to this + optinfo. */ + +void +optinfo::add_dec (const wide_int_ref &wi, signop sgn) +{ + char buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (wi, buf, sgn); + optinfo_item *item + = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + xstrdup (buf), true); + m_items.safe_push (item); +} + +/* Should optinfo instances be created? + All creation of optinfos should be guarded by this predicate. + Return true if any optinfo destinations are active. */ + +bool optinfo_enabled_p () +{ + /* Currently no destinations are implemented, just a hook for + selftests. */ + return dump_context::get ().forcibly_enable_optinfo_p (); +} + +/* Return true if any of the active optinfo destinations make use + of inlining information. + (if true, then the information is preserved). */ + +bool optinfo_wants_inlining_info_p () +{ + return false; +} diff --git a/gcc/optinfo.h b/gcc/optinfo.h new file mode 100644 index 0000000..5bdb9eb --- /dev/null +++ b/gcc/optinfo.h @@ -0,0 +1,203 @@ +/* Optimization information. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#ifndef GCC_OPTINFO_H +#define GCC_OPTINFO_H + +/* An "optinfo" is a bundle of information describing part of an + optimization, which can be emitted to zero or more of several + destinations, such as: + + * as a "remark" through the diagnostics subsystem + + * saved to a file as an "optimization record" + + Currently no such destinations are implemented. + + They are generated in response to calls to the "dump_*" API in + dumpfile.h; repeated calls to the "dump_*" API are consolidated + into a pending optinfo instance, with a "dump_*_loc" starting a new + optinfo instance. + + The data sent to the dump calls are captured within the pending optinfo + instance as a sequence of optinfo_items. For example, given: + + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: live stmt not supported: "); + dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0); + } + + the "dump_printf_loc" call begins a new optinfo containing two items: + (1) a text item containing "not vectorized: live stmt not supported: " + (2) a gimple item for "stmt" + + Dump destinations are thus able to access rich metadata about the + items when the optinfo is emitted to them, rather than just having plain + text. For example, when saving the above optinfo to a file as an + "optimization record", the record could capture the source location of + "stmt" above, rather than just its textual form. + + The currently pending optinfo is emitted and deleted: + * each time a "dump_*_loc" call occurs (which starts the next optinfo), or + * when the dump files are changed (at the end of a pass) + + Dumping to an optinfo instance is non-trivial (due to building optinfo_item + instances), so all usage should be guarded by + + if (optinfo_enabled_p ()) + + which is off by default. */ + + +/* Forward decls. */ +struct opt_pass; +class optinfo_item; /* optinfo-internal.h. */ + +/* Should optinfo instances be created? + All creation of optinfos should be guarded by this predicate. + Return true if any optinfo destinations are active. */ + +extern bool optinfo_enabled_p (); + +/* Return true if any of the active optinfo destinations make use + of inlining information. + (if true, then the information is preserved). */ + +extern bool optinfo_wants_inlining_info_p (); + +/* The various kinds of optinfo. */ + +enum optinfo_kind +{ + OPTINFO_KIND_SUCCESS, + OPTINFO_KIND_FAILURE, + OPTINFO_KIND_NOTE, + OPTINFO_KIND_SCOPE +}; + +extern const char *optinfo_kind_to_string (enum optinfo_kind kind); + +/* A bundle of information describing part of an optimization. */ + +class optinfo +{ + friend class dump_context; + + public: + optinfo (const dump_location_t &loc, + enum optinfo_kind kind, + opt_pass *pass) + : m_loc (loc), m_kind (kind), m_pass (pass), m_items () + {} + ~optinfo (); + + const dump_user_location_t & + get_user_location () const { return m_loc.get_user_location (); } + + const dump_impl_location_t & + get_impl_location () const { return m_loc.get_impl_location (); } + + enum optinfo_kind get_kind () const { return m_kind; } + opt_pass *get_pass () const { return m_pass; } + unsigned int num_items () const { return m_items.length (); } + const optinfo_item *get_item (unsigned int i) const { return m_items[i]; } + + location_t get_location_t () const { return m_loc.get_location_t (); } + profile_count get_count () const { return m_loc.get_count (); } + + private: + void emit (); + + /* Pre-canned ways of manipulating the optinfo, for use by friend class + dump_context. */ + void handle_dump_file_kind (dump_flags_t); + void add_string (const char *str); + void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2; + void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0); + void add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags); + void add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags); + void add_tree (tree node, dump_flags_t dump_flags); + void add_symtab_node (symtab_node *node); + void add_dec (const wide_int_ref &wi, signop sgn); + + template + void add_poly_int (const poly_int &value) + { + /* Compare with dump_dec (MSG_NOTE, ). */ + + STATIC_ASSERT (poly_coeff_traits::signedness >= 0); + signop sgn = poly_coeff_traits::signedness ? SIGNED : UNSIGNED; + + if (value.is_constant ()) + add_dec (value.coeffs[0], sgn); + else + { + add_string ("["); + for (unsigned int i = 0; i < N; ++i) + { + add_dec (value.coeffs[i], sgn); + add_string (i == N - 1 ? "]" : ","); + } + } + } + + private: + dump_location_t m_loc; + enum optinfo_kind m_kind; + opt_pass *m_pass; + auto_vec m_items; +}; + +/* An enum for discriminating between different kinds of optinfo_item. */ + +enum optinfo_item_kind +{ + OPTINFO_ITEM_KIND_TEXT, + OPTINFO_ITEM_KIND_TREE, + OPTINFO_ITEM_KIND_GIMPLE, + OPTINFO_ITEM_KIND_SYMTAB_NODE +}; + +/* An item within an optinfo. */ + +class optinfo_item +{ + public: + optinfo_item (enum optinfo_item_kind kind, location_t location, + char *text, bool owned); + ~optinfo_item (); + + enum optinfo_item_kind get_kind () const { return m_kind; } + location_t get_location () const { return m_location; } + const char *get_text () const { return m_text; } + + private: + /* Metadata (e.g. for optimization records). */ + enum optinfo_item_kind m_kind; + location_t m_location; + + /* The textual form of the item. */ + char *m_text; + bool m_owned; +}; + +#endif /* #ifndef GCC_OPTINFO_H */ From patchwork Wed Jul 11 11:37:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 942426 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-481339-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="bOPmKU5u"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41QbXn1Tx3z9s01 for ; Wed, 11 Jul 2018 20:53:59 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=aj0WB7lXaoyk3ahq7UG8ifoyC+1ZyipdBkdiOkRhv/t9yKTuJLTxX 3RzcP6ofAJFRSVWqNeFytaR/8Mn6CffK0W3KJuCIxw//hpU1oxUsiKFR8bkv9Ezj hyF7YOWafsxXW0Dg3QqyY4cGbss/ezuKMEr/qh4Nr5AJ1XPXS3pvGM= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=NCg+0XRkpKd7raxgriCjRM1Hq+w=; b=bOPmKU5uAngyvhc01IZb 764mr0O1dWCne2XV0FRvd6RSNfH5JvttyB2atlimAvHF2rAB8kIwZ8nhl/UxJrTp O4NagbimuAjm3vAP5R5Vwk5fnzK3ZM7lext5mBEndcyZdxcb7ie6qtb3hQCP3k3W IdjC10IIFhoJU87ZpFS/vIA= Received: (qmail 105129 invoked by alias); 11 Jul 2018 10:53:34 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 105094 invoked by uid 89); 11 Jul 2018 10:53:33 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=2017-2018, 20172018 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 11 Jul 2018 10:53:28 +0000 Received: from smtp.corp.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 10B1830820D2; Wed, 11 Jul 2018 10:53:27 +0000 (UTC) Received: from c64.redhat.com (ovpn-112-31.phx2.redhat.com [10.3.112.31]) by smtp.corp.redhat.com (Postfix) with ESMTP id E8BB13001A49; Wed, 11 Jul 2018 10:53:25 +0000 (UTC) From: David Malcolm To: Richard Biener Cc: GCC Patches , David Malcolm Subject: [PATCH 2/2] Add "-fsave-optimization-record" Date: Wed, 11 Jul 2018 07:37:27 -0400 Message-Id: <1531309047-48505-2-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1531309047-48505-1-git-send-email-dmalcolm@redhat.com> References: <1531309047-48505-1-git-send-email-dmalcolm@redhat.com> X-IsSubscribed: yes This patch implements a -fsave-optimization-record option, which leads to a JSON file being written out, recording the dump_* calls made (via the optinfo infrastructure in the previous patch). The patch includes a minimal version of the JSON patch I posted last year, with just enough support needed for optimization records (I removed all of the parser code, leaving just the code for building in-memory JSON trees and writing them to a pretty_printer). Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. OK for trunk? gcc/ChangeLog: * Makefile.in (OBJS): Add json.o and optinfo-emit-json.o. (CFLAGS-optinfo-emit-json.o): Define TARGET_NAME. * common.opt (fsave-optimization-record): New option. * coretypes.h (struct kv_pair): Move here from dumpfile.c. * doc/invoke.texi (-fsave-optimization-record): New option. * dumpfile.c: Include "optinfo-emit-json.h". (struct kv_pair): Move to coretypes.h. (optgroup_options): Make non-static. (dump_context::end_scope): Call optimization_records_maybe_pop_dump_scope. * dumpfile.h (optgroup_options): New decl. * json.cc: New file. * json.h: New file. * optinfo-emit-json.cc: New file. * optinfo-emit-json.h: New file. * optinfo.cc: Include "optinfo-emit-json.h". (optinfo::emit): Call optimization_records_maybe_record_optinfo. (optinfo_enabled_p): Check optimization_records_enabled_p. (optinfo_wants_inlining_info_p): Likewise. * optinfo.h: Update comment. * profile-count.c (profile_quality_as_string): New function. * profile-count.h (profile_quality_as_string): New decl. (profile_count::quality): New accessor. * selftest-run-tests.c (selftest::run_tests): Call json_cc_tests and optinfo_emit_json_cc_tests. * selftest.h (selftest::json_cc_tests): New decl. (selftest::optinfo_emit_json_cc_tests): New decl. * toplev.c: Include "optinfo-emit-json.h". (compile_file): Call optimization_records_finish. (do_compile): Call optimization_records_start. * tree-ssa-live.c: Include optinfo.h. (remove_unused_scope_block_p): Retain inlining information if optinfo_wants_inlining_info_p returns true. --- gcc/Makefile.in | 3 + gcc/common.opt | 4 + gcc/coretypes.h | 8 + gcc/doc/invoke.texi | 8 +- gcc/dumpfile.c | 15 +- gcc/dumpfile.h | 3 + gcc/json.cc | 293 ++++++++++++++++++++++++ gcc/json.h | 166 ++++++++++++++ gcc/optinfo-emit-json.cc | 568 +++++++++++++++++++++++++++++++++++++++++++++++ gcc/optinfo-emit-json.h | 36 +++ gcc/optinfo.cc | 11 +- gcc/optinfo.h | 4 - gcc/profile-count.c | 28 +++ gcc/profile-count.h | 5 + gcc/selftest-run-tests.c | 2 + gcc/selftest.h | 2 + gcc/toplev.c | 5 + gcc/tree-ssa-live.c | 4 +- 18 files changed, 1143 insertions(+), 22 deletions(-) create mode 100644 gcc/json.cc create mode 100644 gcc/json.h create mode 100644 gcc/optinfo-emit-json.cc create mode 100644 gcc/optinfo-emit-json.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index dd1dfc1..b871640 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1387,6 +1387,7 @@ OBJS = \ ira-color.o \ ira-emit.o \ ira-lives.o \ + json.o \ jump.o \ langhooks.o \ lcm.o \ @@ -1428,6 +1429,7 @@ OBJS = \ optabs-query.o \ optabs-tree.o \ optinfo.o \ + optinfo-emit-json.o \ options-save.o \ opts-global.o \ passes.o \ @@ -2251,6 +2253,7 @@ s-bversion: BASE-VER $(STAMP) s-bversion CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\" +CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\" pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \ $(srcdir)/gen-pass-instances.awk diff --git a/gcc/common.opt b/gcc/common.opt index 5a50bc27..a13c709 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1950,6 +1950,10 @@ fopt-info- Common Joined RejectNegative Var(common_deferred_options) Defer -fopt-info[-=filename] Dump compiler optimization details. +fsave-optimization-record +Common Report Var(flag_save_optimization_record) Optimization +Write a SRCFILE.opt-record.json file detailing what optimizations were performed. + foptimize-register-move Common Ignore Does nothing. Preserved for backward compatibility. diff --git a/gcc/coretypes.h b/gcc/coretypes.h index ed0e825..2fd20e4 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -332,6 +332,14 @@ namespace gcc { typedef std::pair tree_pair; +/* Define a name->value mapping. */ +template +struct kv_pair +{ + const char *const name; /* the name of the value */ + const ValueType value; /* the value of the name */ +}; + #else struct _dont_use_rtx_here_; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 56cd122..c239c53 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}. -freorder-blocks-algorithm=@var{algorithm} @gol -freorder-blocks-and-partition -freorder-functions @gol -frerun-cse-after-loop -freschedule-modulo-scheduled-loops @gol --frounding-math -fsched2-use-superblocks -fsched-pressure @gol +-frounding-math -fsave-optimization-record @gol +-fsched2-use-superblocks -fsched-pressure @gol -fsched-spec-load -fsched-spec-load-dangerous @gol -fsched-stalled-insns-dep[=@var{n}] -fsched-stalled-insns[=@var{n}] @gol -fsched-group-heuristic -fsched-critical-path-heuristic @gol @@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer control of this setting using C99's @code{FENV_ACCESS} pragma. This command-line option will be used to specify the default state for @code{FENV_ACCESS}. +@item -fsave-optimization-record +@opindex fsave-optimization-record +Write a SRCFILE.opt-record.json file detailing what optimizations +were performed. + @item -fsignaling-nans @opindex fsignaling-nans Compile code assuming that IEEE signaling NaNs may generate user-visible diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c index 38f9539..331b7fb 100644 --- a/gcc/dumpfile.c +++ b/gcc/dumpfile.c @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include "dump-context.h" #include "cgraph.h" #include "tree-pass.h" /* for "current_pass". */ +#include "optinfo-emit-json.h" /* If non-NULL, return one past-the-end of the matching SUBPART of the WHOLE string. */ @@ -118,14 +119,6 @@ static struct dump_file_info dump_files[TDI_end] = DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0), }; -/* Define a name->number mapping for a dump flag value. */ -template -struct kv_pair -{ - const char *const name; /* the name of the value */ - const ValueType value; /* the value of the name */ -}; - /* Table of dump options. This must be consistent with the TDF_* flags in dumpfile.h and opt_info_options below. */ static const kv_pair dump_options[] = @@ -176,7 +169,7 @@ static const kv_pair optinfo_verbosity_options[] = }; /* Flags used for -fopt-info groups. */ -static const kv_pair optgroup_options[] = +const kv_pair optgroup_options[] = { {"ipa", OPTGROUP_IPA}, {"loop", OPTGROUP_LOOP}, @@ -794,6 +787,7 @@ dump_context::end_scope () { end_any_optinfo (); m_scope_depth--; + optimization_records_maybe_pop_dump_scope (); } /* Return the optinfo currently being accumulated, creating one if @@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc) } /* End any optinfo that has been accumulated within this context; emitting - it to any destinations as appropriate - though none have currently been - implemented. */ + it to any destinations as appropriate, such as optimization records. */ void dump_context::end_any_optinfo () diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h index 3096a89..e4823f8 100644 --- a/gcc/dumpfile.h +++ b/gcc/dumpfile.h @@ -420,6 +420,7 @@ extern FILE *dump_begin (int, dump_flags_t *); extern void dump_end (int, FILE *); extern int opt_info_switch_p (const char *); extern const char *dump_flag_name (int); +extern const kv_pair optgroup_options[]; /* Global variables used to communicate with passes. */ extern FILE *dump_file; @@ -442,6 +443,7 @@ dump_enabled_p (void) (a) the active dump_file, if any (b) the -fopt-info destination, if any (c) to the "optinfo" destinations, if any: + (c.1) as optimization records dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file | @@ -449,6 +451,7 @@ dump_enabled_p (void) | `--> (c) optinfo `---> optinfo destinations + (c.1) optimization records For optinfos, the dump_*_loc mark the beginning of an optinfo instance: all subsequent dump_* calls are consolidated into diff --git a/gcc/json.cc b/gcc/json.cc new file mode 100644 index 0000000..3c2aa77 --- /dev/null +++ b/gcc/json.cc @@ -0,0 +1,293 @@ +/* JSON trees + Copyright (C) 2017-2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "json.h" +#include "pretty-print.h" +#include "math.h" +#include "selftest.h" + +using namespace json; + +/* class json::value. */ + +/* Dump this json::value tree to OUTF. + No formatting is done. There are no guarantees about the order + in which the key/value pairs of json::objects are printed. */ + +void +value::dump (FILE *outf) const +{ + pretty_printer pp; + pp_buffer (&pp)->stream = outf; + print (&pp); + pp_flush (&pp); +} + +/* class json::object, a subclass of json::value, representing + an unordered collection of key/value pairs. */ + +/* json:object's dtor. */ + +object::~object () +{ + for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) + { + free (const_cast ((*it).first)); + delete ((*it).second); + } +} + +/* Implementation of json::value::print for json::object. */ + +void +object::print (pretty_printer *pp) const +{ + /* Note that the order is not guaranteed. */ + pp_character (pp, '{'); + for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) + { + if (it != m_map.begin ()) + pp_string (pp, ", "); + const char *key = const_cast ((*it).first); + value *value = (*it).second; + pp_printf (pp, "\"%s\": ", key); // FIXME: escaping? + value->print (pp); + } + pp_character (pp, '}'); +} + +/* Set the json::value * for KEY, taking ownership of VALUE + (and taking a copy of KEY if necessary). */ + +void +object::set (const char *key, value *v) +{ + value **ptr = m_map.get (key); + if (ptr) + { + /* If the key is already present, delete the existing value + and overwrite it. */ + delete *ptr; + *ptr = v; + } + else + /* If the key wasn't already present, take a copy of the key, + and store the value. */ + m_map.put (xstrdup (key), v); +} + +/* class json::array, a subclass of json::value, representing + an ordered collection of values. */ + +/* json::array's dtor. */ + +array::~array () +{ + unsigned i; + value *v; + FOR_EACH_VEC_ELT (m_elements, i, v) + delete v; +} + +/* Implementation of json::value::print for json::array. */ + +void +array::print (pretty_printer *pp) const +{ + pp_character (pp, '['); + unsigned i; + value *v; + FOR_EACH_VEC_ELT (m_elements, i, v) + { + if (i) + pp_string (pp, ", "); + v->print (pp); + } + pp_character (pp, ']'); +} + +/* class json::number, a subclass of json::value, wrapping a double. */ + +/* Implementation of json::value::print for json::number. */ + +void +number::print (pretty_printer *pp) const +{ + char tmp[1024]; + snprintf (tmp, sizeof (tmp), "%g", m_value); + pp_string (pp, tmp); +} + +/* class json::string, a subclass of json::value. */ + +void +string::print (pretty_printer *pp) const +{ + pp_character (pp, '"'); + for (const char *ptr = m_utf8; *ptr; ptr++) + { + char ch = *ptr; + switch (ch) + { + case '"': + pp_string (pp, "\\\""); + break; + case '\\': + pp_string (pp, "\\n"); + break; + case '\b': + pp_string (pp, "\\b"); + break; + case '\f': + pp_string (pp, "\\f"); + break; + case '\n': + pp_string (pp, "\\n"); + break; + case '\r': + pp_string (pp, "\\r"); + break; + case '\t': + pp_string (pp, "\\t"); + break; + + default: + pp_character (pp, ch); + } + } + pp_character (pp, '"'); +} + +/* class json::literal, a subclass of json::value. */ + +/* Implementation of json::value::print for json::literal. */ + +void +literal::print (pretty_printer *pp) const +{ + switch (m_kind) + { + case JSON_TRUE: + pp_string (pp, "true"); + break; + case JSON_FALSE: + pp_string (pp, "false"); + break; + case JSON_NULL: + pp_string (pp, "null"); + break; + default: + gcc_unreachable (); + } +} + + +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + +/* Verify that JV->print () prints EXPECTED_JSON. */ + +static void +assert_print_eq (const json::value &jv, const char *expected_json) +{ + pretty_printer pp; + jv.print (&pp); + ASSERT_STREQ (expected_json, pp_formatted_text (&pp)); +} + +/* Verify that JSON objects are written correctly. We can't test more than + one key/value pair, as we don't impose a guaranteed ordering. */ + +static void +test_writing_objects () +{ + object obj; + obj.set ("foo", new json::string ("bar")); + assert_print_eq (obj, "{\"foo\": \"bar\"}"); +} + +/* Verify that JSON arrays are written correctly. */ + +static void +test_writing_arrays () +{ + array arr; + assert_print_eq (arr, "[]"); + + arr.append (new json::string ("foo")); + assert_print_eq (arr, "[\"foo\"]"); + + arr.append (new json::string ("bar")); + assert_print_eq (arr, "[\"foo\", \"bar\"]"); +} + +/* Verify that JSON numbers are written correctly. */ + +static void +test_writing_numbers () +{ + assert_print_eq (number (0), "0"); + assert_print_eq (number (42), "42"); + assert_print_eq (number (-100), "-100"); +} + +/* Verify that JSON strings are written correctly. */ + +static void +test_writing_strings () +{ + string foo ("foo"); + assert_print_eq (foo, "\"foo\""); + + string contains_quotes ("before \"quoted\" after"); + assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\""); +} + +/* Verify that JSON strings are written correctly. */ + +static void +test_writing_literals () +{ + assert_print_eq (literal (JSON_TRUE), "true"); + assert_print_eq (literal (JSON_FALSE), "false"); + assert_print_eq (literal (JSON_NULL), "null"); +} + +/* Run all of the selftests within this file. */ + +void +json_cc_tests () +{ + test_writing_objects (); + test_writing_arrays (); + test_writing_numbers (); + test_writing_strings (); + test_writing_literals (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/json.h b/gcc/json.h new file mode 100644 index 0000000..5c3274c --- /dev/null +++ b/gcc/json.h @@ -0,0 +1,166 @@ +/* JSON trees + Copyright (C) 2017-2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#ifndef GCC_JSON_H +#define GCC_JSON_H + +/* Implementation of JSON, a lightweight data-interchange format. + + See http://www.json.org/ + and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf + and https://tools.ietf.org/html/rfc7159 + + Supports creating a DOM-like tree of json::value *, and then dumping + json::value * to text. */ + +namespace json +{ + +/* Forward decls of json::value and its subclasses (using indentation + to denote inheritance. */ + +class value; + class object; + class array; + class number; + class string; + class literal; + +/* An enum for discriminating the subclasses of json::value. */ + +enum kind +{ + /* class json::object. */ + JSON_OBJECT, + + /* class json::array. */ + JSON_ARRAY, + + /* class json::number. */ + JSON_NUMBER, + + /* class json::string. */ + JSON_STRING, + + /* class json::literal uses these three values to identify the + particular literal. */ + JSON_TRUE, + JSON_FALSE, + JSON_NULL +}; + +/* Base class of JSON value. */ + +class value +{ + public: + virtual ~value () {} + virtual enum kind get_kind () const = 0; + virtual void print (pretty_printer *pp) const = 0; + + void dump (FILE *) const; +}; + +/* Subclass of value for objects: an unordered collection of + key/value pairs. */ + +class object : public value +{ + public: + ~object (); + + enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; } + void print (pretty_printer *pp) const FINAL OVERRIDE; + + void set (const char *key, value *v); + + private: + typedef hash_map > map_t; + map_t m_map; +}; + +/* Subclass of value for arrays. */ + +class array : public value +{ + public: + ~array (); + + enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; } + void print (pretty_printer *pp) const FINAL OVERRIDE; + + void append (value *v) { m_elements.safe_push (v); } + + private: + auto_vec m_elements; +}; + +/* Subclass of value for numbers. */ + +class number : public value +{ + public: + number (double value) : m_value (value) {} + + enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; } + void print (pretty_printer *pp) const FINAL OVERRIDE; + + double get () const { return m_value; } + + private: + double m_value; +}; + +/* Subclass of value for strings. */ + +class string : public value +{ + public: + string (const char *utf8) : m_utf8 (xstrdup (utf8)) {} + ~string () { free (m_utf8); } + + enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; } + void print (pretty_printer *pp) const FINAL OVERRIDE; + + const char *get_string () const { return m_utf8; } + + private: + char *m_utf8; +}; + +/* Subclass of value for the three JSON literals "true", "false", + and "null". */ + +class literal : public value +{ + public: + literal (enum kind kind) : m_kind (kind) {} + + enum kind get_kind () const FINAL OVERRIDE { return m_kind; } + void print (pretty_printer *pp) const FINAL OVERRIDE; + + private: + enum kind m_kind; +}; + +} // namespace json + +#endif /* GCC_JSON_H */ diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc new file mode 100644 index 0000000..bf1172a --- /dev/null +++ b/gcc/optinfo-emit-json.cc @@ -0,0 +1,568 @@ +/* Emit optimization information as JSON files. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#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" + +/* A class for writing out optimization records in JSON format. */ + +class optrecord_json_writer +{ +public: + optrecord_json_writer (); + ~optrecord_json_writer (); + void write () const; + void add_record (const optinfo *optinfo); + void pop_scope (); + + void add_record (json::object *obj); + json::object *impl_location_to_json (dump_impl_location_t loc); + json::object *location_to_json (location_t loc); + json::object *profile_count_to_json (profile_count count); + json::string *get_id_value_for_pass (opt_pass *pass); + json::object *pass_to_json (opt_pass *pass); + json::value *inlining_chain_to_json (location_t loc); + json::object *optinfo_to_json (const optinfo *optinfo); + void add_pass_list (json::array *arr, opt_pass *pass); + +private: + /* The root value for the JSON file. + Currently the JSON values are stored in memory, and flushed when the + compiler exits. It would probably be better to simply write out + the JSON as we go. */ + json::array *m_root_tuple; + + /* The currently open scopes, for expressing nested optimization records. */ + vec m_scopes; +}; + +/* 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 (); + + /* Populate with metadata; compare with toplev.c: print_version. */ + 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? + see gen_producer_string in dwarf2out.c (currently static). */ + + /* 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 +{ + char *filename = concat (dump_base_name, ".opt-record.json", NULL); + FILE *outfile = fopen (filename, "w"); + if (outfile) + { + m_root_tuple->dump (outfile); + fclose (outfile); + } + else + error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs", + filename); // FIXME: more info? + 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. */ + +/* Add record OBJ to the the innermost scope. */ + +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 (); +} + +/* 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)); + obj->set ("line", new json::number (loc.m_line)); + 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) +{ + json::object *obj = new json::object (); + obj->set ("file", new json::string (LOCATION_FILE (loc))); + obj->set ("line", new json::number (LOCATION_LINE (loc))); + obj->set ("column", new json::number (LOCATION_COLUMN (loc))); + 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 (); + obj->set ("value", new json::number (count.to_gcov_type ())); + 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 (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 = optgroup_options; + optgroup->name != NULL; optgroup++) + if (optgroup->value != OPTGROUP_ALL + && (pass->optinfo_flags & optgroup->value)) + optgroups->append (new json::string (optgroup->name)); + } + obj->set ("num", new json::number (pass->static_pass_number)); + 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); + + while (TREE_CODE (ao) == BLOCK + && BLOCK_ABSTRACT_ORIGIN (ao) + && BLOCK_ABSTRACT_ORIGIN (ao) != ao) + ao = BLOCK_ABSTRACT_ORIGIN (ao); + + 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)); + if (*locus != UNKNOWN_LOCATION) + 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. */ + if (item->get_location () != UNKNOWN_LOCATION) + json_item->set ("location", location_to_json (item->get_location ())); + + 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. */ + if (item->get_location () != UNKNOWN_LOCATION) + json_item->set ("location", location_to_json (item->get_location ())); + + 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. */ + if (item->get_location () != UNKNOWN_LOCATION) + json_item->set ("location", location_to_json (item->get_location ())); + 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) + { + const char *fnname = get_fnname_from_decl (current_function_decl); + 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); +} + +/* File-level interface to rest of compiler (to avoid exposing + class optrecord_json_writer outside of this file). */ + +static optrecord_json_writer *the_json_writer; + +/* Perform startup activity for -fsave-optimization-record. */ + +void +optimization_records_start () +{ + /* Bail if recording not enabled. */ + if (!flag_save_optimization_record) + return; + + the_json_writer = new optrecord_json_writer (); +} + +/* Perform cleanup activity for -fsave-optimization-record. + + Currently, the file is written out here in one go, before cleaning + up. */ + +void +optimization_records_finish () +{ + /* Bail if recording not enabled. */ + if (!the_json_writer) + return; + + the_json_writer->write (); + + delete the_json_writer; + the_json_writer = NULL; +} + +/* Did the user request optimization records to be written out? */ + +bool +optimization_records_enabled_p () +{ + return the_json_writer != NULL; +} + +/* If optimization records were requested, then add a record for OPTINFO + to the queue of records to be written. */ + +void +optimization_records_maybe_record_optinfo (const optinfo *optinfo) +{ + /* Bail if recording not enabled. */ + if (!the_json_writer) + return; + + the_json_writer->add_record (optinfo); +} + +/* Handling for the end of a dump scope for the + optimization records sink. */ + +void +optimization_records_maybe_pop_dump_scope () +{ + /* Bail if recording not enabled. */ + if (!the_json_writer) + return; + + the_json_writer->pop_scope (); +} + +#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 () +{ + temp_dump_context tmp (true); + dump_location_t loc; + 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 */ diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h new file mode 100644 index 0000000..3628d56 --- /dev/null +++ b/gcc/optinfo-emit-json.h @@ -0,0 +1,36 @@ +/* Emit optimization information as JSON files. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#ifndef GCC_OPTINFO_EMIT_JSON_H +#define GCC_OPTINFO_EMIT_JSON_H + +class optinfo; +struct opt_pass; + +extern void optimization_records_start (); +extern void optimization_records_finish (); + +extern bool optimization_records_enabled_p (); + +extern void optimization_records_maybe_record_optinfo (const optinfo *); +extern void optimization_records_maybe_pop_dump_scope (); + + +#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */ diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc index 6f224bc..93de9d9 100644 --- a/gcc/optinfo.cc +++ b/gcc/optinfo.cc @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "optinfo.h" +#include "optinfo-emit-json.h" #include "dump-context.h" #include "pretty-print.h" #include "gimple-pretty-print.h" @@ -85,7 +86,8 @@ optinfo::~optinfo () void optinfo::emit () { - /* currently this is a no-op. */ + /* -fsave-optimization-record. */ + optimization_records_maybe_record_optinfo (this); } /* Update the optinfo's kind based on DUMP_KIND. */ @@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn) bool optinfo_enabled_p () { - /* Currently no destinations are implemented, just a hook for - selftests. */ - return dump_context::get ().forcibly_enable_optinfo_p (); + return (dump_context::get ().forcibly_enable_optinfo_p () + || optimization_records_enabled_p ()); } /* Return true if any of the active optinfo destinations make use @@ -232,5 +233,5 @@ bool optinfo_enabled_p () bool optinfo_wants_inlining_info_p () { - return false; + return optimization_records_enabled_p (); } diff --git a/gcc/optinfo.h b/gcc/optinfo.h index 5bdb9eb..5f09022 100644 --- a/gcc/optinfo.h +++ b/gcc/optinfo.h @@ -25,12 +25,8 @@ along with GCC; see the file COPYING3. If not see optimization, which can be emitted to zero or more of several destinations, such as: - * as a "remark" through the diagnostics subsystem - * saved to a file as an "optimization record" - Currently no such destinations are implemented. - They are generated in response to calls to the "dump_*" API in dumpfile.h; repeated calls to the "dump_*" API are consolidated into a pending optinfo instance, with a "dump_*_loc" starting a new diff --git a/gcc/profile-count.c b/gcc/profile-count.c index 3d411cf..6a17f5e 100644 --- a/gcc/profile-count.c +++ b/gcc/profile-count.c @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3. If not see #include "wide-int.h" #include "sreal.h" +/* Get a string describing QUALITY. */ + +const char * +profile_quality_as_string (enum profile_quality quality) +{ + switch (quality) + { + default: + gcc_unreachable (); + case profile_uninitialized: + return "uninitialized"; + case profile_guessed_local: + return "guessed_local"; + case profile_guessed_global0: + return "guessed_global0"; + case profile_guessed_global0adjusted: + return "guessed_global0adjusted"; + case profile_guessed: + return "guessed"; + case profile_afdo: + return "afdo"; + case profile_adjusted: + return "adjusted"; + case profile_precise: + return "precise"; + } +} + /* Dump THIS to F. */ void diff --git a/gcc/profile-count.h b/gcc/profile-count.h index c83fa3b..f4d0c340 100644 --- a/gcc/profile-count.h +++ b/gcc/profile-count.h @@ -59,6 +59,8 @@ enum profile_quality { profile_precise }; +extern const char *profile_quality_as_string (enum profile_quality); + /* The base value for branch probability notes and edge probabilities. */ #define REG_BR_PROB_BASE 10000 @@ -721,6 +723,9 @@ public: return m_quality == profile_precise; } + /* Get the quality of the count. */ + enum profile_quality quality () const { return m_quality; } + /* When merging basic blocks, the two different profile counts are unified. Return true if this can be done without losing info about profile. The only case we care about here is when first BB contains something diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index 7f4d6f3..5adb033 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -72,6 +72,8 @@ selftest::run_tests () typed_splay_tree_c_tests (); unique_ptr_tests_cc_tests (); opt_proposer_c_tests (); + json_cc_tests (); + optinfo_emit_json_cc_tests (); /* Mid-level data structures. */ input_c_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index 54fc488..ede7732 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -228,6 +228,8 @@ extern void gimple_c_tests (); extern void hash_map_tests_c_tests (); extern void hash_set_tests_c_tests (); extern void input_c_tests (); +extern void json_cc_tests (); +extern void optinfo_emit_json_cc_tests (); extern void predict_c_tests (); extern void pretty_print_c_tests (); extern void read_rtl_function_c_tests (); diff --git a/gcc/toplev.c b/gcc/toplev.c index d108096..a047390 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "dumpfile.h" #include "ipa-fnsummary.h" +#include "optinfo-emit-json.h" #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" @@ -487,6 +488,8 @@ compile_file (void) if (lang_hooks.decls.post_compilation_parsing_cleanups) lang_hooks.decls.post_compilation_parsing_cleanups (); + optimization_records_finish (); + if (seen_error ()) return; @@ -2048,6 +2051,8 @@ do_compile () timevar_start (TV_PHASE_SETUP); + optimization_records_start (); + /* This must be run always, because it is needed to compute the FP predefined macros, such as __LDBL_MAX__, for targets using non default FP formats. */ diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c index 7447f7a..2623d9b 100644 --- a/gcc/tree-ssa-live.c +++ b/gcc/tree-ssa-live.c @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "stringpool.h" #include "attribs.h" +#include "optinfo.h" static void verify_live_on_entry (tree_live_info_p); @@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block) ; /* When not generating debug info we can eliminate info on unused variables. */ - else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE) + else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE + && !optinfo_wants_inlining_info_p ()) { /* Even for -g0 don't prune outer scopes from artificial functions, otherwise diagnostics using tree_nonartificial_location