[PATCH 15/16] gcc: Use libgas and libld within the driver

David Malcolm dmalcolm@redhat.com
Mon Jun 1 21:10:00 GMT 2015


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.

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



More information about the Gcc-patches mailing list