/* Support for GCC plugin mechanism.
- Copyright (C) 2009 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
-
-/* If plugin support is not enabled, do not try to execute any code
- that may reference libdl. The generic code is still compiled in to
- avoid including too many conditional compilation paths in the rest
- of the compiler. */
-#ifdef ENABLE_PLUGIN
-#include <dlfcn.h>
-#endif
-
#include "coretypes.h"
-#include "toplev.h"
+#include "hash-table.h"
+#include "diagnostic-core.h"
#include "tree.h"
#include "tree-pass.h"
#include "intl.h"
#include "plugin.h"
-#include "timevar.h"
#include "ggc.h"
#ifdef ENABLE_PLUGIN
const char **plugin_event_name = plugin_event_name_init;
+/* Event hashtable helpers. */
+
+struct event_hasher : typed_noop_remove <const char *>
+{
+ typedef const char *value_type;
+ typedef const char *compare_type;
+ static inline hashval_t hash (const value_type *);
+ static inline bool equal (const value_type *, const compare_type *);
+};
+
+/* Helper function for the event hash table that hashes the entry V. */
+
+inline hashval_t
+event_hasher::hash (const value_type *v)
+{
+ return htab_hash_string (*v);
+}
+
+/* Helper function for the event hash table that compares the name of an
+ existing entry (S1) with the given string (S2). */
+
+inline bool
+event_hasher::equal (const value_type *s1, const compare_type *s2)
+{
+ return !strcmp (*s1, *s2);
+}
+
/* A hash table to map event names to the position of the names in the
plugin_event_name table. */
-static htab_t event_tab;
+static hash_table<event_hasher> *event_tab;
/* Keep track of the limit of allocated events and space ready for
allocating events. */
static struct callback_info *plugin_callbacks_init[PLUGIN_EVENT_FIRST_DYNAMIC];
static struct callback_info **plugin_callbacks = plugin_callbacks_init;
+/* For invoke_plugin_callbacks(), see plugin.h. */
+bool flag_plugin_added = false;
#ifdef ENABLE_PLUGIN
/* Each plugin should define an initialization function with exactly
}
-/* Create a plugin_name_args object for the give plugin and insert it to
- the hash table. This function is called when -fplugin=/path/to/NAME.so
- option is processed. */
+/* Create a plugin_name_args object for the given plugin and insert it
+ to the hash table. This function is called when
+ -fplugin=/path/to/NAME.so or -fplugin=NAME option is processed. */
void
add_new_plugin (const char* plugin_name)
{
struct plugin_name_args *plugin;
void **slot;
- char *base_name = get_plugin_base_name (plugin_name);
+ char *base_name;
+ bool name_is_short;
+ const char *pc;
+
+ flag_plugin_added = true;
+
+ /* Replace short names by their full path when relevant. */
+ name_is_short = !IS_ABSOLUTE_PATH (plugin_name);
+ for (pc = plugin_name; name_is_short && *pc; pc++)
+ if (*pc == '.' || IS_DIR_SEPARATOR (*pc))
+ name_is_short = false;
+
+ if (name_is_short)
+ {
+ base_name = CONST_CAST (char*, plugin_name);
+ /* FIXME: the ".so" suffix is currently builtin, since plugins
+ only work on ELF host systems like e.g. Linux or Solaris.
+ When plugins shall be available on non ELF systems such as
+ Windows or MacOS, this code has to be greatly improved. */
+ plugin_name = concat (default_plugin_dir_name (), "/",
+ plugin_name, ".so", NULL);
+ if (access (plugin_name, R_OK))
+ fatal_error
+ ("inaccessible plugin file %s expanded from short plugin name %s: %m",
+ plugin_name, base_name);
+ }
+ else
+ base_name = get_plugin_base_name (plugin_name);
/* If this is the first -fplugin= option we encounter, create
'plugin_name_args_tab' hash table. */
{
plugin = (struct plugin_name_args *) *slot;
if (strcmp (plugin->full_name, plugin_name))
- error ("Plugin %s was specified with different paths:\n%s\n%s",
+ error ("plugin %s was specified with different paths:\n%s\n%s",
plugin->base_name, plugin->full_name, plugin_name);
return;
}
}
else if (*ptr == '=')
{
- if (key_parsed)
- {
- error ("Malformed option -fplugin-arg-%s (multiple '=' signs)",
- arg);
- return;
- }
- key_len = len;
- len = 0;
- value_start = ptr + 1;
- key_parsed = true;
+ if (!key_parsed)
+ {
+ key_len = len;
+ len = 0;
+ value_start = ptr + 1;
+ key_parsed = true;
+ }
continue;
}
else
if (!key_start)
{
- error ("Malformed option -fplugin-arg-%s (missing -<key>[=<value>])",
+ error ("malformed option -fplugin-arg-%s (missing -<key>[=<value>])",
arg);
return;
}
plugin->argv[plugin->argc - 1].value = value;
}
else
- error ("Plugin %s should be specified before -fplugin-arg-%s "
+ error ("plugin %s should be specified before -fplugin-arg-%s "
"in the command line", name, arg);
/* We don't need the plugin's name anymore. Just release it. */
plugin->help = info->help;
}
-/* Helper function for the event hash table that compares the name of an
- existing entry (E1) with the given string (S2). */
-
-static int
-htab_event_eq (const void *e1, const void *s2)
-{
- const char *s1= *(const char * const *) e1;
- return !strcmp (s1, (const char *) s2);
-}
-
/* Look up the event id for NAME. If the name is not found, return -1
if INSERT is NO_INSERT. */
int
get_named_event_id (const char *name, enum insert_option insert)
{
- void **slot;
+ const char ***slot;
if (!event_tab)
{
int i;
- event_tab = htab_create (150, htab_hash_string, htab_event_eq, NULL);
+ event_tab = new hash_table<event_hasher> (150);
for (i = 0; i < event_last; i++)
{
- slot = htab_find_slot (event_tab, plugin_event_name[i], INSERT);
+ slot = event_tab->find_slot (&plugin_event_name[i], INSERT);
gcc_assert (*slot == HTAB_EMPTY_ENTRY);
*slot = &plugin_event_name[i];
}
}
- slot = htab_find_slot (event_tab, name, insert);
+ slot = event_tab->find_slot (&name, insert);
if (slot == NULL)
return -1;
if (*slot != HTAB_EMPTY_ENTRY)
- return (const char **) *slot - &plugin_event_name[0];
+ return *slot - &plugin_event_name[0];
if (event_last >= event_horizon)
{
plugin_callbacks, event_horizon);
}
/* All the pointers in the hash table will need to be updated. */
- htab_delete (event_tab);
+ delete event_tab;
event_tab = NULL;
}
else
default:
if (event < PLUGIN_EVENT_FIRST_DYNAMIC || event >= event_last)
{
- error ("Unknown callback event registered by plugin %s",
+ error ("unknown callback event registered by plugin %s",
plugin_name);
return;
}
/* Fall through. */
case PLUGIN_FINISH_TYPE:
+ case PLUGIN_FINISH_DECL:
case PLUGIN_START_UNIT:
case PLUGIN_FINISH_UNIT:
- case PLUGIN_CXX_CP_PRE_GENERICIZE:
+ case PLUGIN_PRE_GENERICIZE:
case PLUGIN_GGC_START:
case PLUGIN_GGC_MARKING:
case PLUGIN_GGC_END:
case PLUGIN_EARLY_GIMPLE_PASSES_START:
case PLUGIN_EARLY_GIMPLE_PASSES_END:
case PLUGIN_NEW_PASS:
+ case PLUGIN_INCLUDE_FILE:
{
struct callback_info *new_callback;
if (!callback)
{
- error ("Plugin %s registered a null callback function "
+ error ("plugin %s registered a null callback function "
"for event %s", plugin_name, plugin_event_name[event]);
return;
}
return PLUGEVT_NO_CALLBACK;
}
-/* Called from inside GCC. Invoke all plug-in callbacks registered with
- the specified event.
- Return PLUGEVT_SUCCESS if at least one callback was called,
- PLUGEVT_NO_CALLBACK if there was no callback.
-
- EVENT - the event identifier
- GCC_DATA - event-specific data provided by the compiler */
+/* Invoke all plugin callbacks registered with the specified event,
+ called from invoke_plugin_callbacks(). */
int
-invoke_plugin_callbacks (int event, void *gcc_data)
+invoke_plugin_callbacks_full (int event, void *gcc_data)
{
int retval = PLUGEVT_SUCCESS;
gcc_assert (event < event_last);
/* Fall through. */
case PLUGIN_FINISH_TYPE:
+ case PLUGIN_FINISH_DECL:
case PLUGIN_START_UNIT:
case PLUGIN_FINISH_UNIT:
- case PLUGIN_CXX_CP_PRE_GENERICIZE:
+ case PLUGIN_PRE_GENERICIZE:
case PLUGIN_ATTRIBUTES:
case PLUGIN_PRAGMAS:
case PLUGIN_FINISH:
case PLUGIN_EARLY_GIMPLE_PASSES_START:
case PLUGIN_EARLY_GIMPLE_PASSES_END:
case PLUGIN_NEW_PASS:
+ case PLUGIN_INCLUDE_FILE:
{
/* Iterate over every callback registered with this event and
call it. */
dl_handle = dlopen (plugin->full_name, RTLD_NOW | RTLD_GLOBAL);
if (!dl_handle)
{
- error ("Cannot load plugin %s\n%s", plugin->full_name, dlerror ());
+ error ("cannot load plugin %s\n%s", plugin->full_name, dlerror ());
return false;
}
if ((err = dlerror ()) != NULL)
{
- error ("Cannot find %s in plugin %s\n%s", str_plugin_init_func_name,
+ error ("cannot find %s in plugin %s\n%s", str_plugin_init_func_name,
plugin->full_name, err);
return false;
}
/* Call the plugin-provided initialization routine with the arguments. */
if ((*plugin_init) (plugin, &gcc_version))
{
- error ("Fail to initialize plugin %s", plugin->full_name);
+ error ("fail to initialize plugin %s", plugin->full_name);
return false;
}
/* Dump to FILE the names and associated events for all the active
plugins. */
-void
+DEBUG_FUNCTION void
dump_active_plugins (FILE *file)
{
int event;
for (ci = plugin_callbacks[event]; ci; ci = ci->next)
fprintf (file, " %s", ci->plugin_name);
- putc('\n', file);
+ putc ('\n', file);
}
}
/* Dump active plugins to stderr. */
-void
+DEBUG_FUNCTION void
debug_active_plugins (void)
{
dump_active_plugins (stderr);
}
+/* Give a warning if plugins are present, before an ICE message asking
+ to submit a bug report. */
+
+void
+warn_if_plugins (void)
+{
+ if (plugins_active_p ())
+ {
+ fnotice (stderr, "*** WARNING *** there are active plugins, do not report"
+ " this as a bug unless you can reproduce it without enabling"
+ " any plugins.\n");
+ dump_active_plugins (stderr);
+ }
+
+}
+
+/* Likewise, as a callback from the diagnostics code. */
+
+void
+plugins_internal_error_function (diagnostic_context *context ATTRIBUTE_UNUSED,
+ const char *msgid ATTRIBUTE_UNUSED,
+ va_list *ap ATTRIBUTE_UNUSED)
+{
+ warn_if_plugins ();
+}
+
/* The default version check. Compares every field in VERSION. */
bool
return true;
}
+
/* Return the current value of event_last, so that plugins which provide
additional functionality for events for the benefit of high-level plugins
know how many valid entries plugin_event_name holds. */
{
return event_last;
}
+
+
+/* Retrieve the default plugin directory. The gcc driver should have passed
+ it as -iplugindir <dir> to the cc1 program, and it is queriable through the
+ -print-file-name=plugin option to gcc. */
+const char*
+default_plugin_dir_name (void)
+{
+ if (!plugindir_string)
+ fatal_error ("-iplugindir <dir> option not passed from the gcc driver");
+ return plugindir_string;
+}