On Fri, Nov 14, 2025 at 10:33 AM David Malcolm <[email protected]> wrote: > > This patch adds a new "struct compiler_channels" to hold channels > relating to the compiler that plugins (or diagnostic sinks) might want > to subscribe to events for, accessed from the global gcc::context > object, along with a new gcc/topics/ source subdirectory to hold > strongly-typed publish/subscribe topics relating to the compiler. > > For now, there is just one: pass_events_channel, which, if there are any > subscribers, issues notifications about passes starting/stopping on > particular functions, using topics::pass_events, declared in > topics/pass-events.h, but followup patches add more kinds of > notification channel. > > A toy plugin in the testsuite shows how this could be used to build a > progress notification UI for the compiler, and a followup patch uses the > channel to (optionally) capture CFG information at each stage of > optimization in machine-readable form into a SARIF sink. > > gcc/ChangeLog: > * channels.h: New file. > * context.cc: Define INCLUDE_LIST. Include "channels.h". > (gcc::context::context): Create m_channels. > (gcc::context::~context): Delete it. > * context.h (struct compiler_channels): New forward decl. > (gcc::context::get_channels): New accessor. > (gcc::context::m_channels): New field. > * passes.cc: Define INCLUDE_LIST. Include "topics/pass-events.h" > and "channels.h". > (execute_one_pass): If the global context's pass_events_channel > has subscribers, publish before_pass and after_pass events to it. > * topics/pass-events.h: New file. > > gcc/testsuite/ChangeLog: > * gcc.dg/plugin/plugin.exp: Add progress_notifications_plugin.cc. > * gcc.dg/plugin/progress_notifications_plugin.cc: New test plugin.
The only complaint I have is ... > --- > gcc/channels.h | 43 ++++++++++++++ > gcc/context.cc | 7 ++- > gcc/context.h | 12 ++++ > gcc/passes.cc | 12 ++++ > gcc/testsuite/gcc.dg/plugin/plugin.exp | 1 + > .../plugin/progress_notifications_plugin.cc | 51 ++++++++++++++++ > gcc/topics/pass-events.h | 59 +++++++++++++++++++ > 7 files changed, 184 insertions(+), 1 deletion(-) > create mode 100644 gcc/channels.h > create mode 100644 > gcc/testsuite/gcc.dg/plugin/progress_notifications_plugin.cc > create mode 100644 gcc/topics/pass-events.h > > diff --git a/gcc/channels.h b/gcc/channels.h > new file mode 100644 > index 0000000000000..433a1e7fcf62a > --- /dev/null > +++ b/gcc/channels.h > @@ -0,0 +1,43 @@ > +/* channels.h - Publish/Subscribe channels on compiler-specific topics. > + Copyright (C) 2025 Free Software Foundation, Inc. > + Contributed by David Malcolm <[email protected]>. > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +<http://www.gnu.org/licenses/>. */ > + > +#ifndef GCC_CHANNELS_H > +#define GCC_CHANNELS_H > + > +#include "pub-sub.h" > + > +namespace gcc { > + > +/* Forward decls of subscribers for the various topics we have > + publish/subscribe channels for. */ > +namespace topics { > + namespace pass_events { struct subscriber; } > +} // namespace gcc::topics > + > +/* Publish/subscribe channels on various compiler-specific topics. */ > + > +struct compiler_channels > +{ > + pub_sub::channel<topics::pass_events::subscriber> pass_events_channel; > +}; > + > +} // namespace gcc > + > +#endif /* ! GCC_CHANNELS_H */ > diff --git a/gcc/context.cc b/gcc/context.cc > index f31acc11460eb..cbd7f2bda48e0 100644 > --- a/gcc/context.cc > +++ b/gcc/context.cc > @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public > License > along with GCC; see the file COPYING3. If not see > <http://www.gnu.org/licenses/>. */ > > +#define INCLUDE_LIST > #include "config.h" > #include "system.h" > #include "coretypes.h" > @@ -24,12 +25,15 @@ along with GCC; see the file COPYING3. If not see > #include "pass_manager.h" > #include "dumpfile.h" > #include "realmpfr.h" > +#include "channels.h" > > /* The singleton holder of global state: */ > gcc::context *g; > > gcc::context::context () > - : m_passes (NULL), m_dumps (new gcc::dump_manager ()) > +: m_passes (NULL), > + m_dumps (new gcc::dump_manager ()), > + m_channels (new gcc::compiler_channels ()) > { > have_offload = false; > } > @@ -38,6 +42,7 @@ gcc::context::~context () > { > delete m_passes; > delete m_dumps; > + delete m_channels; > > /* Release MPFR caches to avoid Valgrind leak reports. */ > mpfr_free_cache (); > diff --git a/gcc/context.h b/gcc/context.h > index 2220e517d0c50..e881d565676d1 100644 > --- a/gcc/context.h > +++ b/gcc/context.h > @@ -24,6 +24,7 @@ namespace gcc { > > class pass_manager; > class dump_manager; > +struct compiler_channels; > > /* GCC's internal state can be divided into zero or more > "parallel universe" of state; an instance of this class is one such > @@ -51,6 +52,15 @@ public: > > dump_manager *get_dumps () {gcc_assert (m_dumps); return m_dumps; } > > + /* Publish/subscribe channels for events > + on various compiler-specific topics. */ > + compiler_channels & > + get_channels () const > + { > + gcc_assert (m_channels); > + return *m_channels; > + } > + > private: > /* Pass-management. */ > pass_manager *m_passes; > @@ -58,6 +68,8 @@ private: > /* Dump files. */ > dump_manager *m_dumps; > > + compiler_channels *m_channels; > + > }; // class context > > } // namespace gcc > diff --git a/gcc/passes.cc b/gcc/passes.cc > index a33c8d924a52d..440852f12fabf 100644 > --- a/gcc/passes.cc > +++ b/gcc/passes.cc > @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see > in the proper order, and counts the time used by each. > Error messages and low-level interface to malloc also handled here. */ > > +#define INCLUDE_LIST > #include "config.h" > #include "system.h" > #include "coretypes.h" > @@ -63,6 +64,8 @@ along with GCC; see the file COPYING3. If not see > #include "diagnostic-core.h" /* for fnotice */ > #include "stringpool.h" > #include "attribs.h" > +#include "topics/pass-events.h" > +#include "channels.h" > > /* Reserved TODOs */ > #define TODO_verify_il (1u << 31) > @@ -2575,10 +2578,15 @@ skip_pass (opt_pass *pass) > bool > execute_one_pass (opt_pass *pass) > { > + namespace pass_events = gcc::topics::pass_events; You have this but then ... > + > unsigned int todo_after = 0; > > bool gate_status; > > + if (auto channel = g->get_channels ().pass_events_channel.get_if_active ()) > + channel->publish (gcc::topics::pass_events::before_pass {pass, cfun}); You write out the fully qualified name here. Should it just be pass_events::before_pass ? Also maybe we should be able to do: ``` if (auto channel = pass_events_channel_if_active (g)) ``` Which is slightly easier to write. Obviously it will be a wrapper for `g->get_channels ().pass_events_channel.get_if_active ()`. Thanks, Andrew > + > /* IPA passes are executed on whole program, so cfun should be NULL. > Other passes need function context set. */ > if (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS) > @@ -2741,6 +2749,10 @@ execute_one_pass (opt_pass *pass) > > if (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS) > report_heap_memory_use (); > + > + if (auto channel = g->get_channels ().pass_events_channel.get_if_active ()) > + channel->publish (gcc::topics::pass_events::after_pass {pass, cfun}); > + > return true; > } > > diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp > b/gcc/testsuite/gcc.dg/plugin/plugin.exp > index 38991e8e6191a..b2812db5890ab 100644 > --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp > +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp > @@ -201,6 +201,7 @@ set plugin_test_list [list \ > cpython-plugin-test-PyList_Append.c \ > cpython-plugin-test-PyList_New.c \ > cpython-plugin-test-PyLong_FromLong.c } \ > + { progress_notifications_plugin.cc } \ > ] > > foreach plugin_test $plugin_test_list { > diff --git a/gcc/testsuite/gcc.dg/plugin/progress_notifications_plugin.cc > b/gcc/testsuite/gcc.dg/plugin/progress_notifications_plugin.cc > new file mode 100644 > index 0000000000000..e1fe2a54820f0 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/progress_notifications_plugin.cc > @@ -0,0 +1,51 @@ > +/* Toy implementation of progress notifications about the compiler, > + using gcc::topics::pass_events. */ > + > +#define INCLUDE_LIST > +#include "gcc-plugin.h" > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "tree-pass.h" > +#include "diagnostic-core.h" > +#include "context.h" > +#include "channels.h" > +#include "topics/pass-events.h" > + > +int plugin_is_GPL_compatible; > + > +namespace pass_events = gcc::topics::pass_events; > + > +namespace { > + > +class notification_event_subscriber : public pass_events::subscriber > +{ > +public: > + void on_message (const pass_events::before_pass &m) final override > + { > + if (m.fun) > + inform (m.fun->function_start_locus, "starting pass %qs on %qD", > + m.pass->name, m.fun->decl); > + else > + inform (UNKNOWN_LOCATION, "starting pass %qs", m.pass->name); > + } > + void on_message (const pass_events::after_pass &m) final override > + { > + if (m.fun) > + inform (m.fun->function_end_locus, "finished pass %qs on %qD", > + m.pass->name, m.fun->decl); > + else > + inform (UNKNOWN_LOCATION, "finished pass %qs", m.pass->name); > + } > +} my_event_subscriber; > + > +} // anonymous namespace > + > +int > +plugin_init (struct plugin_name_args *, > + struct plugin_gcc_version *) > +{ > + g->get_channels ().pass_events_channel.add_subscriber > (my_event_subscriber); > + > + return 0; > +} > diff --git a/gcc/topics/pass-events.h b/gcc/topics/pass-events.h > new file mode 100644 > index 0000000000000..2b072f65bc154 > --- /dev/null > +++ b/gcc/topics/pass-events.h > @@ -0,0 +1,59 @@ > +/* pass-events.h - pub/sub messages about GCC optimization passes. > + Copyright (C) 2025 Free Software Foundation, Inc. > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +<http://www.gnu.org/licenses/>. */ > + > +#ifndef GCC_TOPICS_PASS_EVENTS_H > +#define GCC_TOPICS_PASS_EVENTS_H > + > +#include "pub-sub.h" > + > +namespace gcc { > +namespace topics { > + > +/* A topic for messages relating to GCC optimization passes. */ > + > +namespace pass_events { > + > +struct before_pass > +{ > + opt_pass *pass; > + function *fun; > +}; > + > +struct after_pass > +{ > + opt_pass *pass; > + function *fun; > +}; > + > +/* Abstract base class for a subscriber to messages about > + GCC optimization passes. */ > + > +struct subscriber > +{ > + virtual ~subscriber () = default; > + > + virtual void on_message (const before_pass &) = 0; > + virtual void on_message (const after_pass &) = 0; > +}; > + > +} // namespace gcc::topics::pass_events > +} // namespace gcc::topics > +} // namespace gcc > + > +#endif /* ! GCC_TOPICS_PASS_EVENTS_H */ > -- > 2.26.3 >
