Attached please find the patch of our initial prototype of GCC plugin
support based on the APIs described in the (old) wiki page. I also
attached a sample plugin program (dumb-example.c) that shows how a
plugin uses the APIs.

Sean and Taras (and others),

Diego will be creating a branch for the plugin support soon. I know we
still have some issues that have yet to converge (such as flags for
plugin arguments). But in order to get things moving, what do you
think if we start with this patch and move from there? Or if you have
other patches available that implement the currently proposed APIs,
we can start with those too. Thanks.

Le-chun


On Thu, Feb 5, 2009 at 3:27 PM, Taras Glek <tg...@mozilla.com> wrote:
> Le-Chun Wu wrote:
>>
>> Hi Sean,
>>
>> It's great that you updated the wiki page with the latest and more
>> detailed API design.
>>
>> We (at Google) also started to look at the GCC plugin support a couple
>> of weeks ago. We had a quick prototype implemented based on the
>> original APIs that Taras put together in the old wiki. (I can send out
>> the patch later for people's reference.) The updated APIs in general
>> look good to me. I do have some comments based on our experience with
>> the initial prototyping:
>>
>
> Neat! I'd love to see that.
>>
>>
>>>>
>>>> void register_callbacks(int nregistrations, struct plugin_registration
>>>> *registrations);
>>>>
>>>>
>>
>> Instead of passing in an array of plugin_registration objects with a
>> single register_callbacks call, it's probably better to have the
>> plugin call a sequence of register_callback with the necessary
>> information, as shown in the following example:
>>
>> void plugin_register_callback (const char *plugin_name, enum
>> plugin_event event, plugin_callback_func  callback, void *user_data);
>>
>> /* In plugin code */
>> void
>> plugin_init()
>> {
>>  ...
>>  register_callback (plugin_name, PLUGIN_FINISH_STRUCT, handle_struct,
>> NULL);
>>  register_callback (plugin_name, PLUGIN_FINISH_UNIT,
>> handle_end_of_compilation_unit, NULL);
>>  ...
>> }
>>
>> In the function body of register_callback, GCC can then create the
>> plugin_registration objects and chain them together based on the event
>> type. Because GCC will need to maintain a list of plugin_registration
>> objects for each event anyway, we might as well let it create (and
>> destroy if necessary) the objects instead of leaving the task to the
>> plugins.
>>
>> Note that in my example an additional parameter, plugin_name, is added
>> in register_callback. We found it useful to have the name around when
>> emitting error messages if something goes wrong during the callback
>> registration process.
>>
>
> Sean,
> I agree with Le-Chun that separate register_callback calls would be better.
> Would you change that(in the interest of not having too many people modify
> the api), or should I? By the way, reformatting made the page much more
> readable, thanks.
>
> I think having a plugin-name parameter is something we can decide on later (
> just like the version stuff). I can see it being useful for debugging a
> situation with multiple loaded plugins, but I'm not convinced that it will
> be a common problem.
>
>>
>>>>
>>>> -fplugin=/path/to/plugin.so;arg1=value;arg2=value;...
>>>>
>>>>
>>
>> I am not sure if it is GCC's responsibility to understand key-value
>> (or any other types of) arguments to plugins. I think GCC should
>> simply take a string (which, of course, can be something like
>> "arg1=value arg2=value") and pass it (unparsed) to the plugin. It is
>> plugin's job to parse/process the given arguments (whatever way it
>> likes). So the prototype of the plugin_init would look like this:
>>
>> void plugin_init (const char *plugin_name, const char *plugin_arg);
>>
>> In our current prototype, we implemented the originally proposed flag
>> "-fplugin-arg=", which is associated with the plugin specified in the
>> most recently parsed "-fplugin" flag. If a "-fplugin-arg" flag is used
>> in the command-line without any preceding "-fplugin", an error message
>> is emitted. Having the plugin name and its arguments concatenated as
>> currently proposed is also fine, but I would prefer a simple string
>> (like below) to a series of key-value pairs.
>>
>> -fplugin=/path/to/plugin.so;"arguments"
>>
>> (Note that the double quotes may not needed if the "arguments" doesn't
>> contain characters such as spaces.)
>>
>
> I agree with Daniel Jacobowitz's comment that letting every plugin to parse
> command-lines will lead to insanity. I'd be nice to let GCC take care of as
> much as possible there.
>
> From my brief encounter with the complexity of parameter handling code in
> GCC I would be tempted to start with the simplest possible proposal: either
> use -fplugin-arg as I described or stuffing everything into -fplugin and
> using a semi-colon separator.
> Infact, I would prefer not using -fplugin-arg as that leaves more room for
> future implementations to use -fplugin-<name>-<whatever> to do plugin
> arguments.
>>
>>
>>>>
>>>> Pass Manager
>>>>
>>>>
>>
>> We think it would be quite daunting (and probably open up a can of
>> worms) to allow plugins to re-order passes. So to get things moving
>> quickly, in our initial prototype we only support insertion of a new
>> pass and replacing an existing pass. When registering a new pass with
>> GCC, the plugin uses the normal register_callback call with the
>> PLUGIN_PASS_MANAGER_SETUP event. However, instead of registering a
>> callback function, it passes in a plugin_pass object (see below) that
>> specifies the opt_pass object of the new pass, the name of the
>> reference pass, the static instance number of the reference pass, and
>> whether to insert before/after or replacing the reference pass.
>>
>> enum pass_positioning_ops
>> {
>>  PASS_POS_INSERT_AFTER,
>>  PASS_POS_INSERT_BEFORE,
>>  PASS_POS_REPLACE
>> };
>>
>> struct plugin_pass
>> {
>>  struct opt_pass *pass;
>>  const char *reference_pass_name;
>>  /* Insert the pass at the specified instance of the reference pass.
>>     If it's 0, do that for every instance.  */
>>  int ref_pass_instance_number;
>>  enum pass_positioning_ops pos_op;
>> };
>>
>> /* In plugin code */
>> void
>> plugin_init()
>> {
>>  ...
>>  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
>> &pass_info);
>>  ...
>> }
>>
>> When registering and positioning a new pass, GCC will search the
>> passes chained up in all_lowering_passes, all_ipa_passes, and
>> all_passes (similar to Mozilla dehydra's implementation) to find the
>> reference pass(es) with the matching name and instance number, and
>> then either insert the new pass or replace the reference pass.
>>
>> One caveat with our current implementation is that because the
>> registration of new plugin passes happens after the command-line
>> options are parsed, we cannot specify single pass dumping for plugin
>> passes (e.g. -fdump-tree-newpass).  IR dumping of plugin passes is
>> enabled only when the dump-all flags (e.g. -fdump-tree-all) are
>> specified.
>>
>> What do people think about this pass registration/positioning
>> interface that I described? (Again, I will send out our patch later so
>> that people can get a better idea if my description is not clear
>> enough.)
>>
>
> I think a better way to go about it would be to cleanup the existing pass
> manager API to allow plugins to introspect/modify it better. I'd rather
> focus on the bare plugin API at this moment.
>
>
> Cheers,
> Taras
>
Index: gcc/tree-pass.h
===================================================================
--- gcc/tree-pass.h	(revision 143973)
+++ gcc/tree-pass.h	(working copy)
@@ -523,6 +523,7 @@ extern void execute_pass_list (struct op
 extern void execute_ipa_pass_list (struct opt_pass *);
 extern void print_current_pass (FILE *);
 extern void debug_pass (void);
+extern void register_one_dump_file (struct opt_pass *);
 
 /* Set to true if the pass is called the first time during compilation of the
    current function.  Note that using this information in the optimization
Index: gcc/toplev.c
===================================================================
--- gcc/toplev.c	(revision 143973)
+++ gcc/toplev.c	(working copy)
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3.  
 #include "tree-mudflap.h"
 #include "tree-pass.h"
 #include "gimple.h"
+#include "plugin.h"
 
 #if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO)
 #include "dwarf2out.h"
@@ -962,6 +963,7 @@ compile_file (void)
   init_final (main_input_filename);
   coverage_init (aux_base_name);
   statistics_init ();
+  initialize_plugins ();
 
   timevar_push (TV_PARSE);
 
@@ -2193,6 +2195,9 @@ do_compile (void)
 	compile_file ();
 
       finalize ();
+
+      /* Invoke registered plugin callbacks if any.  */
+      invoke_plugin_callbacks (PLUGIN_FINISH_UNIT, NULL);
     }
 
   /* Stop timing and print the times.  */
@@ -2227,6 +2232,9 @@ toplev_main (unsigned int argc, const ch
   if (warningcount || errorcount) 
     print_ignored_options ();
 
+  /* Invoke registered plugin callbacks if any.  */
+  invoke_plugin_callbacks (PLUGIN_FINISH, NULL);
+
   if (errorcount || sorrycount)
     return (FATAL_EXIT_CODE);
 
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 143973)
+++ gcc/cp/decl.c	(working copy)
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  
 #include "timevar.h"
 #include "tree-flow.h"
 #include "pointer-set.h"
+#include "plugin.h"
 
 static tree grokparms (tree parmlist, tree *);
 static const char *redeclaration_error_message (tree, tree);
@@ -12283,6 +12284,8 @@ finish_function (int flags)
   if (!processing_template_decl)
     {
       struct language_function *f = DECL_SAVED_FUNCTION_DATA (fndecl);
+      /* Invoke registered plugin callbacks if any.  */
+      invoke_plugin_callbacks (PLUGIN_CXX_CP_PRE_GENERICIZE, fndecl);
       cp_genericize (fndecl);
       /* Clear out the bits we don't need.  */
       f->x_current_class_ptr = NULL;
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 143973)
+++ gcc/cp/parser.c	(working copy)
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  
 #include "target.h"
 #include "cgraph.h"
 #include "c-common.h"
+#include "plugin.h"
 
 
 /* The lexer.  */
@@ -10998,6 +10999,8 @@ cp_parser_type_specifier (cp_parser* par
       cp_parser_parse_tentatively (parser);
       /* Look for the class-specifier.  */
       type_spec = cp_parser_class_specifier (parser);
+      /* Invoke registered plugin callbacks if any.  */
+      invoke_plugin_callbacks (PLUGIN_FINISH_STRUCT, type_spec);
       /* If that worked, we're done.  */
       if (cp_parser_parse_definitely (parser))
 	{
Index: gcc/c.opt
===================================================================
--- gcc/c.opt	(revision 143973)
+++ gcc/c.opt	(working copy)
@@ -708,6 +708,14 @@ fpermissive
 C++ ObjC++
 Downgrade conformance errors to warnings
 
+fplugin=
+C++ C Joined
+Specify a plugin to load
+
+fplugin-arg=
+C++ C Joined
+Specify arguments to the previously-mentioned plugin
+
 fpreprocessed
 C ObjC C++ ObjC++
 Treat the input file as already preprocessed
Index: gcc/gcc-plugin.h
===================================================================
--- gcc/gcc-plugin.h	(revision 0)
+++ gcc/gcc-plugin.h	(revision 0)
@@ -0,0 +1,64 @@
+/* Public header file for plugins to include.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+   Contributed by Le-Chun Wu <l...@google.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_PLUGIN_H
+#define GCC_PLUGIN_H
+
+enum plugin_event
+{
+  PLUGIN_PASS_MANAGER_SETUP,    /* To hook into pass manager.  */
+  PLUGIN_FINISH_STRUCT,
+  PLUGIN_FINISH_UNIT,           /* Useful for summary processing.  */
+  PLUGIN_CXX_CP_PRE_GENERICIZE, /* Allows to see low level AST in C++ FE.  */
+  PLUGIN_FINISH,                /* Called before GCC exits.  */
+  PLUGIN_EVENT_LAST
+};
+
+enum pass_positioning_ops
+{
+  PASS_POS_INSERT_AFTER,
+  PASS_POS_INSERT_BEFORE,
+  PASS_POS_REPLACE
+};
+
+struct plugin_pass
+{
+  struct opt_pass *pass;
+  const char *reference_pass_name;
+  /* Insert the pass at the specified instance of the reference pass.
+     If it's 0, do that for every instance.  */
+  int ref_pass_instance_number;
+  enum pass_positioning_ops pos_op;
+};
+
+/* Function type for the plugin initialization routine.
+   Returns 0 if initialization finishes successfully.  */
+typedef int (*plugin_init_func) (const char *plugin_name,
+                                 const char *plugin_arg);
+
+/* Function type for the callback routines that plugins register with GCC.  */
+typedef void (*plugin_callback_func) (void *gcc_data, void *user_data);
+
+extern void register_callback (const char *plugin_name,
+                               enum plugin_event event,
+                               plugin_callback_func callback,
+                               void *user_data);
+
+#endif /* GCC_PLUGIN_H */
Index: gcc/c-opts.c
===================================================================
--- gcc/c-opts.c	(revision 143973)
+++ gcc/c-opts.c	(working copy)
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  
 #include "mkdeps.h"
 #include "target.h"
 #include "tm_p.h"
+#include "plugin.h"
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -105,6 +106,9 @@ static size_t deferred_count;
 /* Number of deferred options scanned for -include.  */
 static size_t include_cursor;
 
+/* Current plugin name and its associated argument (if specified).  */
+static struct plugin_name_arg *current_plugin_name_arg = NULL;
+
 static void set_Wimplicit (int);
 static void handle_OPT_d (const char *);
 static void set_std_cxx98 (int);
@@ -742,6 +746,35 @@ c_common_handle_option (size_t scode, co
       flag_permissive = value;
       break;
 
+    case OPT_fplugin_:
+      {
+        struct plugin_name_arg *new_plugin = XCNEW (struct plugin_name_arg);
+        new_plugin->name = arg;
+        if (current_plugin_name_arg)
+          {
+            current_plugin_name_arg->next = new_plugin;
+            current_plugin_name_arg = new_plugin;
+          }
+        else 
+          {
+            plugin_name_arg_list = new_plugin;
+            current_plugin_name_arg = new_plugin;
+          }
+      }
+      break;
+
+    case OPT_fplugin_arg_:
+      if (current_plugin_name_arg)
+        {
+          if (current_plugin_name_arg->arg)
+            error ("-fplugin-arg=%qs should be preceded by -fplugin", arg);
+          else
+            current_plugin_name_arg->arg = arg;
+        }
+      else
+        error ("-fplugin-arg=%qs should be preceded by -fplugin", arg);
+      break;
+
     case OPT_fpreprocessed:
       cpp_opts->preprocessed = value;
       break;
Index: gcc/plugin.c
===================================================================
--- gcc/plugin.c	(revision 0)
+++ gcc/plugin.c	(revision 0)
@@ -0,0 +1,320 @@
+/* Support for GCC plugin mechanism.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+   Contributed by Le-Chun Wu <l...@google.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* This file contains the support for GCC plugin mechanism based on the
+   APIs described in the following wiki page:
+
+   http://gcc.gnu.org/wiki/GCC_PluginAPI  */
+
+#include <dlfcn.h>
+#include <string.h>
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "errors.h"
+#include "tree-pass.h"
+#include "plugin.h"
+
+struct plugin_name_arg *plugin_name_arg_list = NULL;
+
+static const char *str_plugin_init_func_name = "plugin_init";
+
+struct callback_info
+{
+  const char *plugin_name;
+  plugin_callback_func func;
+  void *user_data;
+  struct callback_info *next;
+};
+
+/* An array of lists of callback info objects that is indexed by
+   the event id.  */
+static struct callback_info *plugin_callbacks[PLUGIN_EVENT_LAST] = { NULL };
+
+struct pass_list_node
+{
+  struct opt_pass *pass;
+  struct pass_list_node *next;
+};
+
+/* A list of pass nodes that keep track of all the newly-inserted
+   pass instances so that we can register their dump files after
+   pass-positioning is done.  */
+static struct pass_list_node *added_pass_nodes = NULL;
+static struct pass_list_node *prev_added_pass_node;
+
+
+/* Insert the plugin pass at the proper position. Return true if the pass 
+   is successfully inserted.  */
+static bool
+position_pass (struct plugin_pass *plugin_pass_info,
+               struct opt_pass **pass_list)
+{
+  struct opt_pass *pass = *pass_list, *prev_pass = NULL;
+  bool success = false;
+
+  for ( ; pass; prev_pass = pass, pass = pass->next)
+    {
+      /* See if any exsiting pass matches the name and the instance number
+         of the reference pass.  */
+      if (pass->name
+          && !strcmp (pass->name, plugin_pass_info->reference_pass_name)
+          && ((plugin_pass_info->ref_pass_instance_number == 0)
+              || (plugin_pass_info->ref_pass_instance_number ==
+                  pass->static_pass_number)
+              || (plugin_pass_info->ref_pass_instance_number == 1
+                  && pass->todo_flags_start & TODO_mark_first_instance)))
+        {
+          struct opt_pass *new_pass = plugin_pass_info->pass;
+          struct pass_list_node *new_pass_node;
+          /* The following code (if-statement) is adopted from next_pass_1.  */
+          if (new_pass->static_pass_number)
+            {
+              new_pass = XNEW (struct opt_pass);
+              memcpy (new_pass, plugin_pass_info->pass, sizeof (*new_pass));
+              new_pass->next = NULL;
+
+              new_pass->todo_flags_start &= ~TODO_mark_first_instance;
+
+              plugin_pass_info->pass->static_pass_number -= 1;
+              new_pass->static_pass_number =
+                  -plugin_pass_info->pass->static_pass_number;
+            }
+          else
+            {
+              new_pass->todo_flags_start |= TODO_mark_first_instance;
+              new_pass->static_pass_number = -1;
+            }
+
+          /* Insert the new pass instance based on the positioning op.  */
+          switch (plugin_pass_info->pos_op)
+            {
+              case PASS_POS_INSERT_AFTER:
+                new_pass->next = pass->next;
+                pass->next = new_pass;
+                break;
+              case PASS_POS_INSERT_BEFORE:
+                new_pass->next = pass;
+                if (prev_pass)
+                  prev_pass->next = new_pass;
+                else
+                  *pass_list = new_pass;
+                break;
+              case PASS_POS_REPLACE:
+                new_pass->next = pass->next;
+                if (prev_pass)
+                  prev_pass->next = new_pass;
+                else
+                  *pass_list = new_pass;
+                new_pass->sub = pass->sub;
+                new_pass->tv_id = pass->tv_id;
+                pass = new_pass;
+                /* Do we need to do anything on the memory of the replaced
+                   pass?  */
+                break;
+              default:
+                error ("Invalid pass positioning operation");
+                return false;
+            }
+
+          /* Save the newly added pass (instance) to the added_pass_nodes
+             list so that we can register its dump file later. Note that
+             we cannot register the dump file now as doing so will modify
+             the static_pass_number of the opt_pass object and therefore
+             mess up the dump file name of future instances.  */
+          new_pass_node = XCNEW (struct pass_list_node);
+          new_pass_node->pass = new_pass;
+          if (!added_pass_nodes)
+            added_pass_nodes = new_pass_node;
+          else
+            prev_added_pass_node->next = new_pass_node;
+          prev_added_pass_node = new_pass_node;
+
+          success = true;
+        }
+
+      if (pass->sub && position_pass (plugin_pass_info, &pass->sub))
+        success = true;
+    }
+
+  return success;
+}
+
+
+static void
+register_pass (const char *plugin_name, struct plugin_pass *pass_info)
+{
+  if (!pass_info->pass)
+    {
+      error ("No pass specified when registering a new pass in plugin %s",
+             plugin_name);
+      return;
+    }
+
+  if (!pass_info->reference_pass_name)
+    {
+      error ("No reference pass specified for positioning the pass"
+             " from plugin %s", plugin_name);
+      return;
+    }
+
+  if (!position_pass (pass_info, &all_lowering_passes)
+      && !position_pass (pass_info, &all_ipa_passes)
+      && !position_pass (pass_info, &all_passes))
+    error ("Failed to position pass %s from plugin %s. Cannot find pass %s",
+           pass_info->pass->name, plugin_name, pass_info->reference_pass_name);
+  else
+    {
+      /* Register the dump files for the newly added pass and its duplicates
+         (if any). Because the registration of plugin passes happens after the
+         command-line options are parsed, options that specify single pass
+         dumping (e.g. -fdump-tree-newpass) cannot be used for plugin passes.
+         Therefore we currently can only enable dumping of plugin passes when
+         the dump-all flags (e.g. -fdump-tree-all) are specified.  */
+      while (added_pass_nodes)
+        {
+          struct pass_list_node *next_node = added_pass_nodes->next;
+          enum tree_dump_index tdi;
+          register_one_dump_file (added_pass_nodes->pass);
+          if (added_pass_nodes->pass->type == SIMPLE_IPA_PASS
+              || added_pass_nodes->pass->type == IPA_PASS)
+            tdi = TDI_ipa_all;
+          else if (added_pass_nodes->pass->type == GIMPLE_PASS)
+            tdi = TDI_tree_all;
+          else
+            tdi = TDI_rtl_all;
+          /* Check if dump-all flag is specified.  */
+          if (get_dump_file_info (tdi)->state)
+            get_dump_file_info (added_pass_nodes->pass->static_pass_number)
+                ->state = get_dump_file_info (tdi)->state;
+          XDELETE (added_pass_nodes);
+          added_pass_nodes = next_node;
+        }
+    }
+}
+
+
+void
+register_callback (const char *plugin_name,
+                   enum plugin_event event,
+                   plugin_callback_func callback,
+                   void *user_data)
+{
+  switch (event)
+    {
+      case PLUGIN_PASS_MANAGER_SETUP:
+        register_pass (plugin_name, (struct plugin_pass *) user_data);
+        break;
+      case PLUGIN_FINISH_STRUCT:
+      case PLUGIN_FINISH_UNIT:
+      case PLUGIN_CXX_CP_PRE_GENERICIZE:
+      case PLUGIN_FINISH:
+        {
+          struct callback_info *new_callback;
+          if (!callback)
+            {
+              error ("A null callback function registered by plugin %s",
+                     plugin_name);
+              return;
+            }
+          new_callback = XNEW (struct callback_info);
+          new_callback->plugin_name = plugin_name;
+          new_callback->func = callback;
+          new_callback->user_data = user_data;
+          new_callback->next = plugin_callbacks[event];
+          plugin_callbacks[event] = new_callback;
+        }
+        break;
+      case PLUGIN_EVENT_LAST:
+      default:
+        error ("Unkown callback event registered by plugin %s", plugin_name);
+    }
+}
+
+
+void
+invoke_plugin_callbacks (enum plugin_event event, void *gcc_data)
+{
+  switch (event)
+    {
+      case PLUGIN_FINISH_STRUCT:
+      case PLUGIN_FINISH_UNIT:
+      case PLUGIN_CXX_CP_PRE_GENERICIZE:
+      case PLUGIN_FINISH:
+        {
+          struct callback_info *callback = plugin_callbacks[event];
+          for ( ; callback; callback = callback->next)
+            callback->func (gcc_data, callback->user_data);
+        }
+        break;
+      case PLUGIN_PASS_MANAGER_SETUP:
+      case PLUGIN_EVENT_LAST:
+      default:
+        gcc_assert (false);
+    }
+}
+
+
+void
+initialize_plugins (void)
+{
+  struct plugin_name_arg *plugin, *next;
+  void *pi_handle;
+  plugin_init_func plugin_init;
+  char *err;
+
+  if (!plugin_name_arg_list)
+    return;
+ 
+  for (plugin = plugin_name_arg_list; plugin; plugin = next)
+    {
+      next = plugin->next;
+
+      pi_handle = dlopen (plugin->name, RTLD_LAZY);
+      if (!pi_handle)
+        {
+          error ("Cannot load plugin %s\n%s", plugin->name, dlerror());
+          continue;
+        }
+
+      /* Clear any existing error.  */
+      dlerror();
+
+      plugin_init = dlsym(pi_handle, str_plugin_init_func_name);
+      if ((err = dlerror()) != NULL)
+        {
+          error ("Cannot find %s in plugin %s\n%s", str_plugin_init_func_name,
+                 plugin->name, err);
+          continue;
+        }
+
+      if (plugin_init (plugin->name, plugin->arg))
+        {
+          error ("Fail to initialize plugin %s", plugin->name);
+          continue;
+        }
+
+      /* We can now delete the plugin_name_arg object.  */
+      XDELETE (plugin);
+    }
+
+  plugin_name_arg_list = NULL;
+}
Index: gcc/plugin.h
===================================================================
--- gcc/plugin.h	(revision 0)
+++ gcc/plugin.h	(revision 0)
@@ -0,0 +1,40 @@
+/* Header file for internal GCC plugin mechanism.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+   Contributed by Le-Chun Wu <l...@google.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "gcc-plugin.h"
+
+/* Object that keeps track of the plugin name and argument when parsing
+   the command-line options -fplugin and -fplugin-arg.  */
+struct plugin_name_arg
+{
+  char *name;
+  char *arg;
+  struct plugin_name_arg *next;
+};
+
+extern struct plugin_name_arg *plugin_name_arg_list;
+
+extern void initialize_plugins (void);
+extern void invoke_plugin_callbacks (enum plugin_event event, void *gcc_data);
+
+#endif /* PLUGIN_H */
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 143973)
+++ gcc/Makefile.in	(working copy)
@@ -287,6 +287,9 @@ PPLINC = @PPLINC@
 CLOOGLIBS = @CLOOGLIBS@
 CLOOGINC = @CLOOGINC@
 
+# Libs needed for plugin support
+PLUGINLIBS = -rdynamic -ldl
+
 CPPLIB = ../libcpp/libcpp.a
 CPPINC = -I$(srcdir)/../libcpp/include
 
@@ -914,7 +917,7 @@ BUILD_LIBDEPS= $(BUILD_LIBIBERTY)
 # How to link with both our special library facilities
 # and the system's installed libraries.
 LIBS = @LIBS@ $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBIBERTY) $(LIBDECNUMBER)
-BACKENDLIBS = $(CLOOGLIBS) $(PPLLIBS) $(GMPLIBS)
+BACKENDLIBS = $(CLOOGLIBS) $(PPLLIBS) $(GMPLIBS) $(PLUGINLIBS)
 # Any system libraries needed just for GNAT.
 SYSLIBS = @GNAT_LIBEXC@
 
@@ -1156,6 +1159,7 @@ OBJS-common = \
 	opts.o \
 	params.o \
 	passes.o \
+	plugin.o \
 	pointer-set.o \
 	postreload-gcse.o \
 	postreload.o \
Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 143973)
+++ gcc/passes.c	(working copy)
@@ -368,7 +368,7 @@ get_pass_for_id (int id)
    to do this depth first, and independent of whether the pass is
    enabled or not.  */
 
-static void
+void
 register_one_dump_file (struct opt_pass *pass)
 {
   char *dot_name, *flag_name, *glob_name;
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 143973)
+++ gcc/c-parser.c	(working copy)
@@ -57,6 +57,7 @@ along with GCC; see the file COPYING3.  
 #include "vec.h"
 #include "target.h"
 #include "cgraph.h"
+#include "plugin.h"
 
 
 /* Initialization routine for this file.  */
@@ -1544,6 +1545,8 @@ c_parser_declspecs (c_parser *parser, st
 	  attrs_ok = true;
 	  seen_type = true;
 	  t = c_parser_struct_or_union_specifier (parser);
+          /* Invoke registered plugin callbacks if any.  */
+          invoke_plugin_callbacks (PLUGIN_FINISH_STRUCT, t.spec);
 	  declspecs_add_type (specs, t);
 	  break;
 	case RID_TYPEOF:
/* A trivial GCC plugin example.
   Copyright (C) 2009 Free Software Foundation, Inc.
   Contributed by Le-Chun Wu <l...@google.com>.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-pass.h"
#include "gcc-plugin.h"

extern struct gimple_opt_pass pass_dumb_plugin_example;

/* Callback function to invoke after GCC finishes parsing a struct.  */

void
handle_struct (void *event_data, void *data)
{
  tree type = (tree) event_data;
  printf ("Process struct %s\n",
          IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
}

/* Callback function to invoke before the program is genericized.  */ 

void
handle_pre_generic (void *event_data, void *data)
{
  tree fndecl = (tree) event_data;
  printf ("Before genericizing function %s\n",
          IDENTIFIER_POINTER (DECL_NAME (fndecl)));
}

/* Callback function to invoke after GCC finishes the compilation unit.  */

void
handle_end_of_compilation_unit (void *event_data, void *data)
{
  printf ("End of compilation unit\n");
}

/* Initialization function that GCC calls. This plugin takes an argument
   that specifies the name of the reference pass which the plugin pass should
   be inserted after.  */

int
plugin_init (const char *plugin_name, const char *plugin_arg)
{
  struct plugin_pass pass_info;

  pass_info.pass = &pass_dumb_plugin_example.pass;
  pass_info.reference_pass_name = plugin_arg;
  pass_info.ref_pass_instance_number = 0;
  pass_info.pos_op = PASS_POS_INSERT_AFTER;

  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);

  register_callback (plugin_name, PLUGIN_FINISH_STRUCT, handle_struct, NULL);

  register_callback (plugin_name, PLUGIN_CXX_CP_PRE_GENERICIZE,
                     handle_pre_generic, NULL);

  register_callback (plugin_name, PLUGIN_FINISH_UNIT,
                     handle_end_of_compilation_unit, NULL);
  return 0;
}

static unsigned int
execute_dumb_plugin_example (void)
{
  printf ("Analyze function %s\n",
          IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
  return 0;
}

static bool
gate_dumb_plugin_example (void)
{
  return true;
}

struct gimple_opt_pass pass_dumb_plugin_example =
{
  {
    GIMPLE_PASS,
    "dumb_plugin_example",                /* name */
    gate_dumb_plugin_example,             /* gate */
    execute_dumb_plugin_example,          /* execute */
    NULL,                                 /* sub */
    NULL,                                 /* next */
    0,                                    /* static_pass_number */
    0,                                    /* tv_id */
    PROP_cfg,                             /* properties_required */
    0,                                    /* properties_provided */
    0,                                    /* properties_destroyed */
    0,                                    /* todo_flags_start */
    TODO_dump_func                        /* todo_flags_finish */
  }
};

Reply via email to