On Mon, Jun 1, 2015 at 11:04 PM, David Malcolm <dmalc...@redhat.com> wrote: > This patch adds the ability for gcc to be configured with: > --with-embedded-as > --with-embedded-ld > If so, invocations of "as" and "ld" are detected in the gcc driver, and > specialcased by invoking these in-process as shared libraries. This is > intended for use by libgccjit, when the driver itself is in-process > within libgccjit, eliminating fork/exec and dynamic-library resolution. > > Doing so dramatically speeds up jit.dg/test-benchmark.c. > > The patch generalizes the named items support within timevar.c, so that > as well as having bucket of named "jit client items" we also have > buckets for "as" and for "ld" so that they can account for time spent > within them. > > One remaining hack here, appending CFLAGS-gcc.o with a hardcoded include > path, but I didn't want that to hold up posting what I've got so far.
Hum, so why not go further and embed as into cc1/cc1plus, etc.? That is, make the as invocation parts of the driver accessible to the compiler in some way. This way we can eventually add a more efficient way of funneling the compiler assembler output to libas (well, I suppose you at least use -pipe...). Richard. > gcc/ChangeLog: > * configure.ac: Add --with-embedded-as and --with-embedded-ld. > * gcc.c: Include libgas.h and libld.h. > (class ctimershim): New. > (ctimershim::impl_push): New. > (ctimershim::impl_pop): New. > (run_embedded_as): New. > (run_embedded_ld): New. > (enum known_command): New. > (get_known_command): New. > (tv_id_for_known_command): New. > (maybe_run_embedded_command): New. > (execute): Invoke get_known_command and > maybe_run_embedded_command, potentially avoiding the need to call > into pex. > * timevar.c (timer::named_items::print): Add "name" param rather > than hardcoding "Client items". > (timer::timer): Initialize "m_has_named_items"; replace > "m_jit_client_items" with "m_named_items" array. > (timer::~timer): Likewise. > (timer::push_client_item): Rename to... > (timer::push_named_item): ...this and add "dict" param, > generalizing to support an array of dicts of named items. > (timer::pop_client_item): Rename to... > (timer::pop_named_item): ...this, generalizing to support > an array of dicts of named items. > (timer::print): Print JIT client items first (if any), then > GCC timevar items, then embedded as items (if any), then embedded > ld items (if any). > * timevar.def (TV_DRIVER_EMBEDDED_AS): New. > (TV_DRIVER_EMBEDDED_LD): New. > * timevar.h (timer::item_dict): New enum. > (timer::push_client_item): Rename to... > (timer::push_named_item): ...this, adding "dict" param. > (timer::pop_client_item): Rename to... > (timer::pop_named_item): ...this, adding "dict" param. > (timer::get_item_dict): New. > (timer::m_jit_client_items): Drop this field in favor of... > (timer::m_named_items): ...this array. > (timer::m_has_named_items): New. > > gcc/jit/ChangeLog: > * Make-lang.in (LIBGCCJIT_FILENAME): Add EXTRA_GCC_LIBS to link. > * libgccjit.c (gcc_jit_timer_push): Replace call to > timer->push_client_item with timer->push_named_item. > (gcc_jit_timer_pop): Likewise for pop. > * notes.txt: Indicate that as/ld could be embedded. > --- > gcc/Makefile.in | 3 + > gcc/configure.ac | 25 ++++++ > gcc/gcc.c | 214 > ++++++++++++++++++++++++++++++++++++++++++++++++--- > gcc/jit/Make-lang.in | 2 +- > gcc/jit/libgccjit.c | 5 +- > gcc/jit/notes.txt | 4 +- > gcc/timevar.c | 56 ++++++++++---- > gcc/timevar.def | 2 + > gcc/timevar.h | 33 +++++++- > 9 files changed, 308 insertions(+), 36 deletions(-) > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in > index 2388975..9061933 100644 > --- a/gcc/Makefile.in > +++ b/gcc/Makefile.in > @@ -1993,6 +1993,9 @@ DRIVER_DEFINES = \ > > CFLAGS-gcc.o += $(DRIVER_DEFINES) > > +# FIXME > +CFLAGS-gcc.o += > -I/home/david/coding/gcc-python/binutils-gdb-libraries/install/include > + > specs.h : s-specs ; @true > s-specs : Makefile > lsf="$(lang_specs_files)"; for f in $$lsf; do \ > diff --git a/gcc/configure.ac b/gcc/configure.ac > index 810725c..6f50908 100644 > --- a/gcc/configure.ac > +++ b/gcc/configure.ac > @@ -1114,6 +1114,31 @@ LIBS= > AC_SEARCH_LIBS(kstat_open, kstat) > EXTRA_GCC_LIBS="$LIBS" > LIBS="$save_LIBS" > + > +# Support embedding libgas in the driver > + > +AC_ARG_WITH([embedded-as], > + [AS_HELP_STRING([--with-embedded-as], > + [use libgas to embed the assembler in-process])], > + [AC_CHECK_LIB([gas], [gas_main], > + [EXTRA_GCC_LIBS+=" -lgas $LDFLAGS"; > + AC_DEFINE(HAVE_LIBGAS, 1, > + [Define if libgas is installed.]) > + ], > + [AC_MSG_ERROR(["libgas not found"])])]) > + > +# Support embedding libld in the driver > + > +AC_ARG_WITH([embedded-ld], > + [AS_HELP_STRING([--with-embedded-ld], > + [use libld to embed the linker in-process])], > + [AC_CHECK_LIB([ld], [ld_main], > + [EXTRA_GCC_LIBS+=" -lld $LDFLAGS"; > + AC_DEFINE(HAVE_LIBLD, 1, > + [Define if libld is installed.]) > + ], > + [AC_MSG_ERROR(["libld not found"])])]) > + > AC_SUBST(EXTRA_GCC_LIBS) > > # Some systems put ldexp and frexp in libm instead of libc; assume > diff --git a/gcc/gcc.c b/gcc/gcc.c > index 93f41ec..ed92c7d 100644 > --- a/gcc/gcc.c > +++ b/gcc/gcc.c > @@ -45,6 +45,14 @@ compilation is specified by a string called a "spec". */ > #include "filenames.h" > #include "timevar.h" > > +#ifdef HAVE_LIBGAS > +#include "libgas.h" > +#endif > + > +#ifdef HAVE_LIBLD > +#include "libld.h" > +#endif > + > /* Singleton instance of "driver" class. */ > static driver *g_driver; > > @@ -2807,6 +2815,190 @@ add_sysrooted_prefix (struct path_prefix *pprefix, > const char *prefix, > require_machine_suffix, os_multilib); > } > > + > +/* An implementation of the ctimer hooks C API, forwarding to > + our C++ "timer" class, for a particular timer::item_dict. */ > +class ctimershim : public ctimer > +{ > + public: > + ctimershim (timer *t, > + enum timer::item_dict dict) > + : m_timer (t), > + m_dict (dict) > + { > + this->push = impl_push; > + this->pop = impl_pop; > + } > + > + private: > + static void impl_push (ctimer *that, const char *item_name); > + static void impl_pop (ctimer *that); > + > + private: > + timer *m_timer; > + enum timer::item_dict m_dict; > +}; > + > +/* Implement CTIMER_PUSH in terms of pushing a named item > + within the given item_dict. */ > +void > +ctimershim::impl_push (ctimer *that, const char *item_name) > +{ > + ctimershim *this_ = static_cast <ctimershim *> (that); > + gcc_assert (this_->m_timer); > + this_->m_timer->push_named_item (this_->m_dict, item_name); > +} > + > +/* Implement CTIMER_POP in terms of popping the item > + from the given item_dict. */ > +void > +ctimershim::impl_pop (ctimer *that) > +{ > + ctimershim *this_ = static_cast <ctimershim *> (that); > + gcc_assert (this_->m_timer); > + this_->m_timer->pop_named_item (this_->m_dict); > +} > + > +#ifdef HAVE_LIBGAS > + > +/* Invoke gas_main, passing in the driver's timer > + so that the gas code can record timing information into it. */ > + > +static int run_embedded_as (int argc, const char **argv) > +{ > + gcc_assert (g_driver); > + timer *driver_timer = g_driver->get_timer (); > + auto_timevar tv (driver_timer, TV_DRIVER_EMBEDDED_AS); > + if (0) > + { > + fprintf (stderr, "run_embedded_as: %i args\n", argc); > + for (int i = 0; i < argc; i++) > + fprintf (stderr, " argv[%i]: %s\n", i, argv[i]); > + } > + > + ctimershim ct (driver_timer, timer::ITEM_DICT_EMBEDDED_AS); > + return gas_main (argc, > + argv, > + 0, /* "standalone" */ > + driver_timer ? &ct : NULL); /* "timer" */ > +} > + > +#endif /* #ifdef HAVE_LIBGAS */ > + > +#ifdef HAVE_LIBLD > + > +/* Invoke ld_main, passing in the driver's timer > + so that the linker code can record timing information into it. */ > + > +static int run_embedded_ld (int argc, const char **argv) > +{ > + gcc_assert (g_driver); > + timer *driver_timer = g_driver->get_timer (); > + auto_timevar tv (driver_timer, TV_DRIVER_EMBEDDED_LD); > + if (0) > + { > + fprintf (stderr, "run_embedded_ld: %i args\n", argc); > + for (int i = 0; i < argc; i++) > + fprintf (stderr, " argv[%i]: %s\n", i, argv[i]); > + } > + > + ctimershim ct (driver_timer, timer::ITEM_DICT_EMBEDDED_LD); > + return ld_main (argc, > + argv, > + 0, /* "standalone" */ > + driver_timer ? &ct : NULL); /* struct ctimer * */ > +} > + > +#endif /* #ifdef HAVE_LIBLD */ > + > +/* The result of get_known_command. */ > + > +enum known_command > +{ > + KNOWN_COMMAND_AS, > + KNOWN_COMMAND_COLLECT2, > + KNOWN_COMMAND_LD, > + KNOWN_COMMAND_OTHER, /* not a known command, or a pipeline. */ > + NUM_KNOWN_COMMANDS > +}; > + > +/* Do we have an invocation of a single, known command, with no pipes? > + We can use this for selecting a timevar_id_t for the pex invocation, > + and potentially for running the command in-process. */ > + > +static enum known_command > +get_known_command (int n_commands, const char *argv0) > +{ > + if (n_commands == 1) > + { > + if (0 == strcmp (argv0, "as")) > + return KNOWN_COMMAND_AS; > + else if (0 == strcmp (argv0, "collect2")) > + return KNOWN_COMMAND_COLLECT2; > + else if (0 == strcmp (argv0, "ld")) > + return KNOWN_COMMAND_LD; > + } > + return KNOWN_COMMAND_OTHER; > +} > + > +/* If we're timing things, and we have a single command in the > + pipeline, bill the time to that command. Given that we > + need a timevar for each one, we only split out a few important > + commands. */ > + > +const timevar_id_t tv_id_for_known_command[NUM_KNOWN_COMMANDS] = { > + TV_DRIVER_EXECUTE_AS, > + TV_DRIVER_EXECUTE_COLLECT2, > + TV_DRIVER_EXECUTE_LD, > + TV_DRIVER_EXECUTE_OTHER > +}; > + > +/* Optimization: if we have a single, known command, and we're linked > + against an embedded copy of it, call it directly in-process, avoiding > + the overhead of fork/exec/dynamic link. > + > + Return true if an embedded command was run, writing the result to > + *RESULT_OUT. > + Return false if no embedded command was available, leading *result_out > + untouched. */ > + > +static bool > +maybe_run_embedded_command (enum known_command known_command, > + int *result_out ATTRIBUTE_UNUSED) > +{ > +#if defined (HAVE_LIBGAS) || defined (HAVE_LIBLD) > + int argc = argbuf.length (); > + const char **argv = argbuf.address (); > +#endif > + > + switch (known_command) > + { > + case KNOWN_COMMAND_AS: > +#ifdef HAVE_LIBGAS > + *result_out = run_embedded_as (argc, argv); > + return true; > +#else > + return false; > +#endif > + > + case KNOWN_COMMAND_COLLECT2: > + return false; > + > + case KNOWN_COMMAND_LD: > +#ifdef HAVE_LIBLD > + *result_out = run_embedded_ld (argc, argv); > + return true; > +#else > + return false; > +#endif > + > + default: > + gcc_unreachable (); > + case KNOWN_COMMAND_OTHER: > + return false; > + } > +} > + > /* Execute the command specified by the arguments on the current line of > spec. > When using pipes, this includes several piped-together commands > with `|' between them. > @@ -2845,23 +3037,23 @@ execute (void) > if (strcmp (arg, "|") == 0) > n_commands++; > > + /* Determine if we're dealing with a single embeddable command. */ > + enum known_command known_command = get_known_command (n_commands, > + argbuf[0]); > + > + /* Optimization: potentially avoid fork/exec by calling the command > + as a function in-process. */ > + int result; > + if (maybe_run_embedded_command (known_command, &result)) > + return result; > + > /* If we're timing things, and we have a single command in the > pipeline, bill the time to that command. Given that we > need a timevar for each one, we only split out a few important > commands. */ > - timevar_id_t tv_id; > - tv_id = TV_DRIVER_EXECUTE_OTHER; > gcc_assert (g_driver); > timer *driver_timer = g_driver->get_timer (); > - if (driver_timer && n_commands == 1) > - { > - if (0 == strcmp (argbuf[0], "as")) > - tv_id = TV_DRIVER_EXECUTE_AS; > - else if (0 == strcmp (argbuf[0], "collect2")) > - tv_id = TV_DRIVER_EXECUTE_COLLECT2; > - else if (0 == strcmp (argbuf[0], "ld")) > - tv_id = TV_DRIVER_EXECUTE_LD; > - } > + timevar_id_t tv_id = tv_id_for_known_command[known_command]; > auto_timevar tv (driver_timer, tv_id); > > /* Get storage for each command. */ > diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in > index 9cff752..a7efe2e 100644 > --- a/gcc/jit/Make-lang.in > +++ b/gcc/jit/Make-lang.in > @@ -84,7 +84,7 @@ $(LIBGCCJIT_FILENAME): $(jit_OBJS) \ > +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \ > $(jit_OBJS) libbackend.a libcommon-target.a libcommon.a \ > $(CPPLIB) $(LIBDECNUMBER) $(LIBS) $(BACKENDLIBS) \ > - $(EXTRA_GCC_OBJS) \ > + $(EXTRA_GCC_OBJS) $(EXTRA_GCC_LIBS) \ > -Wl,--version-script=$(srcdir)/jit/libgccjit.map \ > -Wl,-soname,$(LIBGCCJIT_SONAME) > > diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c > index 2a67ef7..8eee2da 100644 > --- a/gcc/jit/libgccjit.c > +++ b/gcc/jit/libgccjit.c > @@ -2431,7 +2431,8 @@ gcc_jit_timer_push (gcc_jit_timer *timer, > { > RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer"); > RETURN_IF_FAIL (item_name, NULL, NULL, "NULL item_name"); > - timer->push_client_item (item_name); > + timer->push_named_item (timer::ITEM_DICT_JIT_CLIENT_CODE, > + item_name); > } > > /* Pop the top item from the timing stack. */ > @@ -2441,7 +2442,7 @@ gcc_jit_timer_pop (gcc_jit_timer *timer) > { > RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer"); > /* FIXME: mismatching item? */ > - timer->pop_client_item (); > + timer->pop_named_item (timer::ITEM_DICT_JIT_CLIENT_CODE); > } > > /* Print timing information to the given stream about activity since > diff --git a/gcc/jit/notes.txt b/gcc/jit/notes.txt > index 36e05cb..4469145 100644 > --- a/gcc/jit/notes.txt > +++ b/gcc/jit/notes.txt > @@ -81,8 +81,8 @@ Client Code . Generated . libgccjit.so > . . --> Convert assembler to DSO, via embedded > . . copy of driver: > . . driver::main () > - . . invocation of "as" > - . . invocation of "ld" > + . . in-process libgas, or out-of-process > invocation of "as" > + . . in-process libld, or out-of-process > invocation of "ld" > . . driver::finalize () > . . <---- > . . │ . . > diff --git a/gcc/timevar.c b/gcc/timevar.c > index 9bc62e6..28db5b4 100644 > --- a/gcc/timevar.c > +++ b/gcc/timevar.c > @@ -134,7 +134,7 @@ class timer::named_items > > void push (const char *item_name); > void pop (); > - void print (FILE *fp, const timevar_time_def *total); > + void print (FILE *fp, const char *name, const timevar_time_def *total); > > private: > /* Which timer instance does this relate to? */ > @@ -197,11 +197,13 @@ timer::named_items::pop () > /* Print the given client item. Helper function for timer::print. */ > > void > -timer::named_items::print (FILE *fp, const timevar_time_def *total) > +timer::named_items::print (FILE *fp, > + const char *name, > + const timevar_time_def *total) > { > unsigned int i; > const char *item_name; > - fprintf (fp, "Client items:\n"); > + fprintf (fp, "%s:\n", name); > FOR_EACH_VEC_ELT (m_names, i, item_name) > { > timer::timevar_def *def = m_hash_map.get (item_name); > @@ -260,11 +262,14 @@ timer::timer () : > m_stack (NULL), > m_unused_stack_instances (NULL), > m_start_time (), > - m_jit_client_items (NULL) > + m_has_named_items (false) > { > /* Zero all elapsed times. */ > memset (m_timevars, 0, sizeof (m_timevars)); > > + /* There are no named_timers yet. */ > + memset (m_named_items, 0, sizeof (m_named_items)); > + > /* Initialize the names of timing variables. */ > #define DEFTIMEVAR(identifier__, name__) \ > m_timevars[identifier__].name = name__; > @@ -298,7 +303,8 @@ timer::~timer () > free (iter); > } > > - delete m_jit_client_items; > + for (int i = 0; i < NUM_ITEM_DICTS; i++) > + delete m_named_items[i]; > } > > /* Initialize timing variables. */ > @@ -544,24 +550,32 @@ timer::cond_stop (timevar_id_t timevar) > /* Push the named item onto the timing stack. */ > > void > -timer::push_client_item (const char *item_name) > +timer::push_named_item (enum item_dict dict, > + const char *item_name) > { > + gcc_assert (dict < NUM_ITEM_DICTS); > gcc_assert (item_name); > - > /* Lazily create the named_items instance. */ > - if (!m_jit_client_items) > - m_jit_client_items = new named_items (this); > + named_items **item_dict = &m_named_items[dict]; > + if (!*item_dict) > + *item_dict = new named_items (this); > + (*item_dict)->push (item_name); > > - m_jit_client_items->push (item_name); > + m_has_named_items = true; > } > > /* Pop the top-most client item from the timing stack. */ > > void > -timer::pop_client_item () > +timer::pop_named_item (enum item_dict dict) > { > - gcc_assert (m_jit_client_items); > - m_jit_client_items->pop (); > + gcc_assert (dict < NUM_ITEM_DICTS); > + named_items *item_dict = m_named_items[dict]; > + > + /* If this fails, we have a pop from something that was never pushed to. > */ > + gcc_assert (item_dict); > + > + item_dict->pop (); > } > > /* Validate that phase times are consistent. */ > @@ -687,7 +701,11 @@ timer::print (FILE *fp) > m_start_time = now; > > fputs ("\nExecution times (seconds)\n", fp); > - if (m_jit_client_items) > + if (m_named_items[ITEM_DICT_JIT_CLIENT_CODE]) > + m_named_items[ITEM_DICT_JIT_CLIENT_CODE]->print (fp, > + "Client items", > + total); > + if (m_has_named_items) > fputs ("GCC items:\n", fp); > for (id = 0; id < (unsigned int) TIMEVAR_LAST; ++id) > { > @@ -713,8 +731,14 @@ timer::print (FILE *fp) > > print_row (fp, total, tv); > } > - if (m_jit_client_items) > - m_jit_client_items->print (fp, total); > + if (m_named_items[ITEM_DICT_EMBEDDED_AS]) > + m_named_items[ITEM_DICT_EMBEDDED_AS]->print (fp, > + "Embedded 'as'", > + total); > + if (m_named_items[ITEM_DICT_EMBEDDED_LD]) > + m_named_items[ITEM_DICT_EMBEDDED_LD]->print (fp, > + "Embedded 'ld'", > + total); > > /* Print total time. */ > fputs (" TOTAL :", fp); > diff --git a/gcc/timevar.def b/gcc/timevar.def > index ce9236d..2360b30 100644 > --- a/gcc/timevar.def > +++ b/gcc/timevar.def > @@ -292,8 +292,10 @@ DEFTIMEVAR (TV_DRIVER_SETUP , "driver: setup") > DEFTIMEVAR (TV_DRIVER_SPEC , "driver: do spec on infiles") > DEFTIMEVAR (TV_DRIVER_LINK , "driver: run linker") > DEFTIMEVAR (TV_DRIVER_EXECUTE_AS , "driver: executing \"as\"") > +DEFTIMEVAR (TV_DRIVER_EMBEDDED_AS , "driver: embedded assembler") > DEFTIMEVAR (TV_DRIVER_EXECUTE_COLLECT2, "driver: executing \"collect2\"") > DEFTIMEVAR (TV_DRIVER_EXECUTE_LD , "driver: executing \"ld\"") > +DEFTIMEVAR (TV_DRIVER_EMBEDDED_LD , "driver: embedded linker") > DEFTIMEVAR (TV_DRIVER_EXECUTE_OTHER , "driver: executing subprocess") > DEFTIMEVAR (TV_LINK , "link JIT code") > DEFTIMEVAR (TV_LOAD , "load JIT result") > diff --git a/gcc/timevar.h b/gcc/timevar.h > index d46dc88..a0133d2 100644 > --- a/gcc/timevar.h > +++ b/gcc/timevar.h > @@ -105,6 +105,16 @@ extern void timevar_cond_stop (timevar_id_t, bool); > class timer > { > public: > + /* Support for multiple collections of named timing items. */ > + enum item_dict > + { > + ITEM_DICT_JIT_CLIENT_CODE, > + ITEM_DICT_EMBEDDED_AS, > + ITEM_DICT_EMBEDDED_LD, > + NUM_ITEM_DICTS > + }; > + > + public: > timer (); > ~timer (); > > @@ -115,12 +125,20 @@ class timer > bool cond_start (timevar_id_t tv); > void cond_stop (timevar_id_t tv); > > - void push_client_item (const char *item_name); > - void pop_client_item (); > + void push_named_item (enum item_dict dict, > + const char *item_name); > + void pop_named_item (enum item_dict dict); > > void print (FILE *fp); > > private: > + /* A class for managing a collection of named timing items, for use > + e.g. by libgccjit for timing client code. This class is declared > + inside timevar.c to avoid everything using timevar.h > + from needing vec and hash_map. */ > + class named_items; > + > + private: > /* Private member functions. */ > void validate_phases (FILE *fp) const; > > @@ -131,6 +149,8 @@ class timer > const timevar_time_def *total, > const timevar_def *tv); > > + named_items *get_item_dict (enum item_dict dict); > + > private: > > /* Private type: a timing variable. */ > @@ -193,10 +213,15 @@ class timer > element. */ > timevar_time_def m_start_time; > > - /* If non-NULL, for use when timing libgccjit's client code. */ > - named_items *m_jit_client_items; > + /* Array of named_items, which are created on demand, and hence > + may each be NULL/non-NULL. */ > + named_items *m_named_items[NUM_ITEM_DICTS]; > > friend class named_items; > + > + /* Do we have any named items? Affects the output of the "print" > + method. */ > + bool m_has_named_items; > }; > > /* Provided for backward compatibility. */ > -- > 1.8.5.3 >