[PATCH 00/16] RFC: Embedding as and ld inside gcc driver and into libgccjit

classic Classic list List threaded Threaded
27 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/16] RFC: Embedding as and ld inside gcc driver and into libgccjit

David Malcolm
[Crossposting to both gcc-patches and binutils lists, since this
patch kit touches both source trees].

Binutils devs: GCC 5 gained a way to build GCC as a shared library,
libgccjit.so.

I'm been experimenting with ways of optimizing libgccjit, and the
following patch kit (touching both gcc and binutils) achieves a 5x
speedup of
  gcc/testsuite/jit.dg/test-benchmark.c
on this x86_64 box (Fedora 20).

The benchmark constructs IR for a simple function in memory, compiles
it, and runs it, 100 times in a row, in the hope of simulating the
workload of an interpreter/VM/language runtime, where bytecode
functions gradually become "hot" (e.g. interpretation count exceeds
a threshold) and are compiled to machine code, all within one
process.

gcc's backend code emits .s files, and libgccjit currently use pex to
invoke the gcc driver to turn it from .s to a .so file (which in
turn invokes "as" and "ld").

These invocations dominate the time take by libgccjit, so the patch
series attempts to time them, and to move them in-process; doing
so largely eliminates the cost of them.

Here are the performance gains:

jit.dg/test-benchmark.c, 100 iterations at optlevel 0:
 Without embedded driver:      wallclock of 5.300s (0.053s per iteration)
 With embedded driver:         wallclock of 4.630s (0.046s per iteration)
 With embedded driver & gas:   wallclock of 3.510s (0.035s per iteration)
 With embedded driver&as&ld:   wallclock of 2.130s (0.021s per iteration)
 As above, hacking up ld args: wallclock of 1.030s (0.010s per iteration)

i.e. about 5x speedup.

There are some memory leaks, FIXMEs, etc, and it hasn't been fully
tested yet, but I thought it was time to post this for discussion.

The patch kit also generalizes gcc's timevar mechanism in such a way
that it can be used both by jit client code, and by "as" and "ld".  An
example of a combined report on the accumulated timings of 100
iterations of jit.dg/test-benchmark.c at optlevel 0:

Execution times (seconds)
Client items:
 test_jit                :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 create_code             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 compile                 :   0.21 (30%) usr   0.13 (45%) sys   0.25 (25%) wall   14939 kB (74%) ggc
 verify_code             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
GCC items:
 phase setup             :   0.15 (22%) usr   0.02 ( 7%) sys   0.15 (15%) wall   10661 kB (53%) ggc
 phase parsing           :   0.02 ( 3%) usr   0.00 ( 0%) sys   0.02 ( 2%) wall     653 kB ( 3%) ggc
 callgraph construction  :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 2%) wall     242 kB ( 1%) ggc
 callgraph optimization  :   0.01 ( 1%) usr   0.01 ( 3%) sys   0.01 ( 1%) wall     142 kB ( 1%) ggc
 cfg construction        :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall      17 kB ( 0%) ggc
 cfg cleanup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 df live regs            :   0.02 ( 3%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 df reg dead/unused notes:   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall      23 kB ( 0%) ggc
 register information    :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 parser (global)         :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall     199 kB ( 1%) ggc
 tree eh                 :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 tree CFG construction   :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall     196 kB ( 1%) ggc
 tree operand scan       :   0.00 ( 0%) usr   0.01 ( 3%) sys   0.00 ( 0%) wall     100 kB ( 0%) ggc
 out of ssa              :   0.00 ( 0%) usr   0.02 ( 7%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 expand                  :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall     398 kB ( 2%) ggc
 loop init               :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall      67 kB ( 0%) ggc
 integrated RA           :   0.07 (10%) usr   0.02 ( 7%) sys   0.02 ( 2%) wall    2468 kB (12%) ggc
 LRA virtuals elimination:   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall      56 kB ( 0%) ggc
 machine dep reorg       :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 shorten branches        :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.02 ( 2%) wall       0 kB ( 0%) ggc
 final                   :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall     216 kB ( 1%) ggc
 initialize rtl          :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall      12 kB ( 0%) ggc
 rest of compilation     :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 3%) wall     232 kB ( 1%) ggc
 unaccounted todo        :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.02 ( 2%) wall       0 kB ( 0%) ggc
 replay of JIT client activity:   0.01 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall     309 kB ( 2%) ggc
 driver                  :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 driver: setup           :   0.04 ( 6%) usr   0.00 ( 0%) sys   0.06 ( 6%) wall       0 kB ( 0%) ggc
 driver: do spec on infiles:   0.01 ( 1%) usr   0.00 ( 0%) sys   0.02 ( 2%) wall       0 kB ( 0%) ggc
 driver: run linker      :   0.00 ( 0%) usr   0.01 ( 3%) sys   0.02 ( 2%) wall       0 kB ( 0%) ggc
 driver: embedded assembler:   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 driver: embedded linker :   0.04 ( 6%) usr   0.02 ( 7%) sys   0.04 ( 4%) wall       0 kB ( 0%) ggc
 load JIT result         :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
Embedded 'as':
 gas_main                :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 before pass             :   0.03 ( 4%) usr   0.02 ( 7%) sys   0.13 (13%) wall       0 kB ( 0%) ggc
 perform_an_assembly_pass:   0.06 ( 9%) usr   0.01 ( 3%) sys   0.06 ( 6%) wall       0 kB ( 0%) ggc
 after pass              :   0.04 ( 6%) usr   0.00 ( 0%) sys   0.03 ( 3%) wall       0 kB ( 0%) ggc
 cleanup                 :   0.02 ( 3%) usr   0.00 ( 0%) sys   0.03 ( 3%) wall       0 kB ( 0%) ggc
Embedded 'ld':
 ld_internal_main: init  :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 ldmain.c: lang_final    :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 ldmain.c: lang_process  :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 lang_process: 1st half  :   0.00 ( 0%) usr   0.02 ( 7%) sys   0.02 ( 2%) wall       0 kB ( 0%) ggc
 open_output             :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 open_input_bfds         :   0.01 ( 1%) usr   0.02 ( 7%) sys   0.01 ( 1%) wall       0 kB ( 0%) ggc
 lang_input_statement_enum:   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 open_input_bfds:load_symbols:   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 load_symbols: ldfile_open_file:   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 ldlang_add_file         :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 load_symbols: bfd_link_add_symbols:   0.02 ( 3%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 lang_process: 2nd half  :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.04 ( 4%) wall       0 kB ( 0%) ggc
 ldmain.c: ldwrite       :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 3%) wall       0 kB ( 0%) ggc
 ld_main cleanup         :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 TOTAL                 :   0.69             0.29             0.99              20298 kB

Thoughts?

--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 05/16] gcc: driver: add g_driver singleton

David Malcolm
gcc/ChangeLog:
        * gcc.c (g_driver): New.
        (driver::driver): Set "g_driver".
        (driver::~driver): Unset "g_driver".
---
 gcc/gcc.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/gcc/gcc.c b/gcc/gcc.c
index 7314317..46e750d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -45,6 +45,9 @@ compilation is specified by a string called a "spec".  */
 #include "filenames.h"
 #include "timevar.h"
 
+/* Singleton instance of "driver" class.  */
+static driver *g_driver;
+
 
 
 /* Manage the manipulation of env vars.  We poison getenv and putenv, so that
@@ -7029,6 +7032,9 @@ driver::driver (bool can_finalize, bool debug, timer *t) :
   decoded_options (NULL),
   m_timer (t)
 {
+  gcc_assert (g_driver == NULL);
+  g_driver = this;
+
   env.init (can_finalize, debug);
 }
 
@@ -7036,6 +7042,9 @@ driver::~driver ()
 {
   XDELETEVEC (explicit_link_files);
   XDELETEVEC (decoded_options);
+
+  gcc_assert (g_driver == this);
+  g_driver = NULL;
 }
 
 /* driver::main is implemented as a series of driver:: method calls.  */
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 01/16] gcc: Generalization of timevar API; add gcc_jit_timer interface

David Malcolm
In reply to this post by David Malcolm
In r223092 (aka dd4d567f4b6b498242097c41d63666bdae320ac1) I moved the
state of timevar.c from being global data into a "class timer".

This followup patch generalizes the timing within toplev so that an
external "timer" instance can be passed in, and then exposes this
from libgccjit via an API for client code: gcc_jit_timer.

The gcc_jit_timer API allows client code to create arbitrary new timing
items (beyond the hardcoded list in timevar.def), giving them names,
so that client code can time whatever things are relevant to it.

It uses this API within jit.dg/test-benchmark.c to accumulate timings,
comparing the different optimization levels.  For example, in one run,
the output indicated:
  ./test-benchmark.c.exe: 100 iterations
    at optlevel 0 took a total of 6.370s (0.064s per iteration)
    at optlevel 1 took a total of 7.050s (0.071s per iteration)
    at optlevel 2 took a total of 7.260s (0.073s per iteration)
    at optlevel 3 took a total of 9.070s (0.091s per iteration)
with detailed timings being emitted for each optlevel.

Further patches in this kit speed this up dramatically, and use the
named items API in other ways.

The patch also generalizes timevar.h's auto_timevar to avoid it
depending on the "g_timer" global (which is only set during the
lifetime of "toplev".  We can use this to add TV_JIT_ACQUIRING_MUTEX,
and thus measure wallclock time spent waiting for the JIT mutex.

ABI note: this extends the libgccjit API for the first time since GCC 5;
should there be a SONAME bump?  Is there a better way of managing this
(e.g. via a linker script) to avoid invalidating binaries compiled
against previous versions of the library?

gcc/ChangeLog:
        * main.c (main): Pass in NULL for toplev's external_timer.
        * timevar.c: Include coretypes.h, hash-map.h, vec.h.
        (class timer::named_items): New.
        (timer::named_items::named_items): New.
        (timer::named_items::~named_items): New.
        (timer::named_items::push): New.
        (timer::named_items::pop): New.
        (timer::named_items::print): New.
        (timer::timer): Initialize field "m_jit_client_items".
        (timer::~timer): New.
        (timer::push): Move bulk of implementation to...
        (timer::push_internal): ...here.  New function.
        (timer::pop): Move bulk of implementation to...
        (timer::pop_internal): ...here.  New function.
        (timer::push_client_item): New.
        (timer::pop_client_item): New.
        (timer::print_row): New function, taken from timer::print.
        (timer::print): Print "GCC items" header if we also have client
        items.  Move row-printing to timer::print_row.  Print any client
        items.
        * timevar.def (TV_JIT_ACQUIRING_MUTEX): New.
        (TV_JIT_CLIENT_CODE): New.
        * timevar.h (timer::push_client_item): New declaration.
        (timer::pop_client_item): New declaration.
        (timer::push_internal): New declaration.
        (timer::pop_internal): New declaration.
        (timer::print_row): New declaration.
        (timer::named_items): New declaration.
        (timer::m_jit_client_items): New field.
        (timer): Add friend class named_items.
        (auto_timevar::auto_timevar): Add timer param.
        (auto_timevar::~auto_timevar): Use field "m_timer".
        (auto_timevar::m_timer): New field.
        * toplev.c (initialize_rtl): Add g_timer as param when
        constructing auto_timevar instance.
        (toplev::toplev): Add "external_timer" param, and use it to
        initialize the "g_timer" global if non-NULL.
        (toplev::~toplev): If this created "g_timer", delete it.
        * toplev.h (toplev::toplev): Replace "use_TV_TOTAL" bool param
        with "external_timer" timer *.

gcc/jit/ChangeLog:
        * docs/topics/contexts.rst (GCC_JIT_BOOL_OPTION_DUMP_SUMMARY):
        We no longer show a profile.
        * docs/topics/index.rst (Topic Reference): Add performance.rst.
        * docs/topics/performance.rst: New file.
        * jit-playback.c (gcc::jit::playback::context::compile): Add timer
        param when constructing the "toplev" instance.
        (gcc::jit::playback::context::acquire_mutex): Add timer param when
        constructing auto_timevar instance.
        (gcc::jit::playback::context::make_fake_args): If we have a timer,
        add "-ftime-report".
        (gcc::jit::playback::context::invoke_driver): Add timer param when
        constructing auto_timevar instance.
        (gcc::jit::playback::context::dlopen_built_dso): Likewise.
        * jit-playback.h (gcc::jit::playback::context::get_timer): New accessor.
        * jit-recording.c: Include timevar.h.
        (gcc::jit::recording::context::context): Initialize field "m_timer".
        * jit-recording.h: Add forward declaration of class timer.
        (gcc::jit::recording::context::set_timer): New method.
        (gcc::jit::recording::context::get_timer): New method.
        (gcc::jit::recording::context::m_timer): New field.
        * libgccjit++.h (gccjit::timer): New class.
        (gccjit::auto_time): New class.
        (gccjit::context::set_timer): New method.
        (gccjit::context::get_timer): New.
        (gccjit::timer::timer): New.
        (gccjit::timer::push): New.
        (gccjit::timer::pop): New.
        (timer::print): New.
        (timer::get_inner_timer): New.
        (timer::release): New.
        (auto_time::auto_time): New.
        (auto_time::~auto_time): New.
        * libgccjit.c: Include timevar.h.
        (struct gcc_jit_timer): New.
        (gcc_jit_timer_new): New function.
        (gcc_jit_timer_release): New function.
        (gcc_jit_context_set_timer): New function.
        (gcc_jit_context_get_timer): New function.
        (gcc_jit_timer_push): New function.
        (gcc_jit_timer_pop): New function.
        (gcc_jit_timer_print): New function.
        * libgccjit.h (LIBGCCJIT_HAS_FEATURE_gcc_jit_timer): New macro.
        (gcc_jit_timer): New typedef.
        (gcc_jit_timer_new): New function.
        (gcc_jit_timer_release): New function.
        (gcc_jit_context_set_timer): New function.
        (gcc_jit_context_get_timer): New function.
        (gcc_jit_timer_push): New function.
        (gcc_jit_timer_pop): New function.
        (gcc_jit_timer_print): New function.
        * libgccjit.map (gcc_jit_timer_new): New function.
        (gcc_jit_timer_release): New function.
        (gcc_jit_context_set_timer): New function.
        (gcc_jit_context_get_timer): New function.
        (gcc_jit_timer_push): New function.
        (gcc_jit_timer_pop): New function.
        (gcc_jit_timer_print): New function.

gcc/testsuite/ChangeLog:
        * jit.dg/test-benchmark.c (test_jit): Add param "timer" and use
        it to push/pop timing items.
        (main): For each optimization level, create a gcc_jit_timer, and
        time all of the iteration within that level cumulatively.
---
 gcc/jit/docs/topics/contexts.rst      |   3 +-
 gcc/jit/docs/topics/index.rst         |   1 +
 gcc/jit/docs/topics/performance.rst   | 208 ++++++++++++++++++++++++++++++
 gcc/jit/jit-playback.c                |  11 +-
 gcc/jit/jit-playback.h                |   2 +
 gcc/jit/jit-recording.c               |   2 +
 gcc/jit/jit-recording.h               |   7 +
 gcc/jit/libgccjit++.h                 | 114 ++++++++++++++++
 gcc/jit/libgccjit.c                   |  90 +++++++++++++
 gcc/jit/libgccjit.h                   |  51 ++++++++
 gcc/jit/libgccjit.map                 |   7 +
 gcc/main.c                            |   2 +-
 gcc/testsuite/jit.dg/test-benchmark.c |  20 ++-
 gcc/timevar.c                         | 236 +++++++++++++++++++++++++++++-----
 gcc/timevar.def                       |   2 +
 gcc/timevar.h                         |  33 ++++-
 gcc/toplev.c                          |  18 ++-
 gcc/toplev.h                          |   4 +-
 18 files changed, 757 insertions(+), 54 deletions(-)
 create mode 100644 gcc/jit/docs/topics/performance.rst

diff --git a/gcc/jit/docs/topics/contexts.rst b/gcc/jit/docs/topics/contexts.rst
index b7f281a..afa51a9 100644
--- a/gcc/jit/docs/topics/contexts.rst
+++ b/gcc/jit/docs/topics/contexts.rst
@@ -417,8 +417,7 @@ Boolean options
   .. macro:: GCC_JIT_BOOL_OPTION_DUMP_SUMMARY
 
      If true, :func:`gcc_jit_context_compile` will print information to stderr
-     on the actions it is performing, followed by a profile showing
-     the time taken and memory usage of each phase.
+     on the actions it is performing.
 
   .. macro:: GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING
 
diff --git a/gcc/jit/docs/topics/index.rst b/gcc/jit/docs/topics/index.rst
index 4ebb623..b221e40 100644
--- a/gcc/jit/docs/topics/index.rst
+++ b/gcc/jit/docs/topics/index.rst
@@ -28,3 +28,4 @@ Topic Reference
    functions.rst
    locations.rst
    compilation.rst
+   performance.rst
diff --git a/gcc/jit/docs/topics/performance.rst b/gcc/jit/docs/topics/performance.rst
new file mode 100644
index 0000000..b7de28e
--- /dev/null
+++ b/gcc/jit/docs/topics/performance.rst
@@ -0,0 +1,208 @@
+.. Copyright (C) 2015 Free Software Foundation, Inc.
+   Originally contributed by David Malcolm <[hidden email]>
+
+   This 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 of the License, or
+   (at your option) any later version.
+
+   This program 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 this program.  If not, see
+   <http://www.gnu.org/licenses/>.
+
+.. default-domain:: c
+
+Performance
+===========
+
+The timing API
+--------------
+
+As of GCC 6, libgccjit exposes a timing API, for printing reports on
+how long was spent in different parts of code.
+
+You can create a :c:type:`gcc_jit_timer` instance, which will
+measure time spent since its creation.  The timer maintains a stack
+of "timer items": as control flow moves through your code, you can push
+and pop named items relating to your code onto the stack, and the timer
+will account the time spent accordingly.
+
+You can also asssociate a timer with a :c:type:`gcc_jit_context`, in
+which case the time spent inside compilation will be subdivided.
+
+For example, the following code uses a timer, recording client items
+"create_code", "compile", and "running code":
+
+.. code-block:: c
+
+  /* Create a timer.  */
+  gcc_jit_timer *timer = gcc_jit_timer_new ();
+  if (!timer)
+    {
+       error ("gcc_jit_timer_new failed");
+       return -1;
+    }
+
+  /* Let's repeatedly compile and run some code, accumulating it
+     all into the timer.  */
+  for (int i = 0; i < num_iterations; i++)
+    {
+      /* Create a context and associate it with the timer.  */
+      gcc_jit_context *ctxt = gcc_jit_context_acquire ();
+      if (!ctxt)
+        {
+          error ("gcc_jit_context_acquire failed");
+          return -1;
+        }
+      gcc_jit_context_set_timer (ctxt, timer);
+
+      /* Populate the context, timing it as client item "create_code".  */
+      gcc_jit_timer_push (timer, "create_code");
+      create_code (ctxt);
+      gcc_jit_timer_pop (timer);
+
+      /* Compile the context, timing it as client item "compile".  */
+      gcc_jit_timer_push (timer, "compile");
+      result = gcc_jit_context_compile (ctxt);
+      gcc_jit_timer_pop (timer);
+
+      /* Run the generated code, timing it as client item "running code".  */
+      gcc_jit_timer_push (timer, "running code");
+      run_the_code (ctxt, result);
+      gcc_jit_timer_pop (timer);
+
+      /* Clean up.  */
+      gcc_jit_context_release (ctxt);
+      gcc_jit_result_release (result);
+  }
+
+  /* Print the accumulated timings.  */
+  gcc_jit_timer_print (timer, stderr);
+  gcc_jit_timer_release (timer);
+
+giving output like this, showing the internal GCC items at the top, then
+client items, then the total::
+
+  Execution times (seconds)
+  GCC items:
+   phase setup             :   0.29 (14%) usr   0.00 ( 0%) sys   0.32 ( 5%) wall   10661 kB (50%) ggc
+   phase parsing           :   0.02 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall     653 kB ( 3%) ggc
+   phase finalize          :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   dump files              :   0.02 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
+   callgraph construction  :   0.02 ( 1%) usr   0.01 ( 6%) sys   0.01 ( 0%) wall     242 kB ( 1%) ggc
+   callgraph optimization  :   0.03 ( 2%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall     142 kB ( 1%) ggc
+   trivially dead code     :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   df scan insns           :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       9 kB ( 0%) ggc
+   df live regs            :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
+   inline parameters       :   0.02 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall      82 kB ( 0%) ggc
+   tree CFG cleanup        :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   tree PHI insertion      :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall      64 kB ( 0%) ggc
+   tree SSA other          :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall      18 kB ( 0%) ggc
+   expand                  :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall     398 kB ( 2%) ggc
+   jump                    :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   loop init               :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall      67 kB ( 0%) ggc
+   integrated RA           :   0.02 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall    2468 kB (12%) ggc
+   thread pro- & epilogue  :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall     162 kB ( 1%) ggc
+   final                   :   0.01 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall     216 kB ( 1%) ggc
+   rest of compilation     :   1.37 (69%) usr   0.00 ( 0%) sys   1.13 (18%) wall    1391 kB ( 6%) ggc
+   assemble JIT code       :   0.01 ( 1%) usr   0.00 ( 0%) sys   4.04 (66%) wall       0 kB ( 0%) ggc
+   load JIT result         :   0.02 ( 1%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   JIT client code         :   0.00 ( 0%) usr   0.01 ( 6%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+  Client items:
+   create_code             :   0.00 ( 0%) usr   0.01 ( 6%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   compile                 :   0.36 (18%) usr   0.15 (83%) sys   0.86 (14%) wall   14939 kB (70%) ggc
+   running code            :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
+   TOTAL                   :   2.00             0.18             6.12              21444 kB
+
+The exact format is intended to be human-readable, and is subject to change.
+
+.. macro:: LIBGCCJIT_HAS_FEATURE_gcc_jit_timer
+
+   The timer API was added to libgccjit in GCC 6.
+   This macro is only defined in versions of libgccjit.h which have the
+   timer API, and so can be used to guard code that may need to compile
+   against earlier releases::
+
+     #ifdef LIBGCCJIT_HAS_FEATURE_gcc_jit_timer
+     gcc_jit_timer *t = gcc_jit_timer_new ();
+     gcc_jit_context_set_timer (ctxt, t);
+     #endif
+
+.. type:: gcc_jit_timer
+
+.. function:: gcc_jit_timer * gcc_jit_timer_new(void)
+
+   Create a :c:type:`gcc_jit_timer` instance, and start timing::
+
+     gcc_jit_timer *t = gcc_jit_timer_new ();
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: void gcc_jit_timer_release(gcc_jit_timer *timer)
+
+   Release a :c:type:`gcc_jit_timer` instance::
+
+     gcc_jit_timer_release (t);
+
+   This should be called exactly once on a timer.
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: void gcc_jit_context_set_timer(gcc_jit_context *ctxt, \
+                                             gcc_jit_timer *timer)
+
+   Associate a :c:type:`gcc_jit_timer` instance with a context::
+
+      gcc_jit_context_set_timer (ctxt, t);
+
+   A timer instance can be shared between multiple
+   :c:type:`gcc_jit_context` instances.
+
+   Timers have no locking, so if you have a multithreaded program, you
+   must provide your own locks if more than one thread could be working
+   with the same timer via timer-associated contexts.
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: gcc_jit_timer *gcc_jit_context_get_timer(gcc_jit_context *ctxt)
+
+   Get the timer associated with a context (if any).
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: void gcc_jit_timer_push(gcc_jit_timer *timer, \
+                                      const char *item_name)
+
+   Push the given item onto the timer's stack::
+
+      gcc_jit_timer_push (t, "running code");
+      run_the_code (ctxt, result);
+      gcc_jit_timer_pop (t);
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: void gcc_jit_timer_pop(gcc_jit_timer *timer)
+
+   Pop the top item from the timer's stack.
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
+
+.. function:: void gcc_jit_timer_print(gcc_jit_timer *timer, \
+                                       FILE *f_out)
+
+   Print timing information to the given stream about activity since
+   the timer was started.
+
+   .. versionadded:: 6
+      See :macro:`LIBGCCJIT_HAS_FEATURE_gcc_jit_timer`.
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index c75c076..7a3b7f7 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -1727,7 +1727,7 @@ compile ()
     }
 
   /* This runs the compiler.  */
-  toplev toplev (false, /* use_TV_TOTAL */
+  toplev toplev (get_timer (), /* external_timer */
  false); /* init_signals */
   enter_scope ("toplev::main");
   if (get_logger ())
@@ -2019,6 +2019,8 @@ static pthread_mutex_t jit_mutex = PTHREAD_MUTEX_INITIALIZER;
 void
 playback::context::acquire_mutex ()
 {
+  auto_timevar tv (get_timer (), TV_JIT_ACQUIRING_MUTEX);
+
   /* Acquire the big GCC mutex. */
   JIT_LOG_SCOPE (get_logger ());
   pthread_mutex_lock (&jit_mutex);
@@ -2186,6 +2188,9 @@ make_fake_args (vec <char *> *argvec,
       }
   }
 
+  if (get_timer ())
+    ADD_ARG ("-ftime-report");
+
 #undef ADD_ARG
 #undef ADD_ARG_TAKE_OWNERSHIP
 }
@@ -2310,7 +2315,7 @@ invoke_driver (const char *ctxt_progname,
   JIT_LOG_SCOPE (get_logger ());
   /* Currently this lumps together both assembling and linking into
      TV_ASSEMBLE.  */
-  auto_timevar assemble_timevar (tv_id);
+  auto_timevar assemble_timevar (get_timer (), tv_id);
   const char *errmsg;
   auto_vec <const char *> argvec;
 #define ADD_ARG(arg) argvec.safe_push (arg)
@@ -2390,7 +2395,7 @@ playback::context::
 dlopen_built_dso ()
 {
   JIT_LOG_SCOPE (get_logger ());
-  auto_timevar load_timevar (TV_LOAD);
+  auto_timevar load_timevar (get_timer (), TV_LOAD);
   void *handle = NULL;
   const char *error = NULL;
   result *result_obj = NULL;
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index e9832f0..afe0068 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -219,6 +219,8 @@ public:
   void write_global_decls_1 ();
   void write_global_decls_2 ();
 
+  timer *get_timer () const { return m_recording_ctxt->get_timer (); }
+
 private:
   void dump_generated_code ();
 
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index 32d7f31..79a22e8 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pretty-print.h"
 #include "hash-map.h"
 #include "toplev.h"
+#include "timevar.h"
 
 #include <pthread.h>
 
@@ -461,6 +462,7 @@ recording::context::context (context *parent_ctxt)
   : log_user (NULL),
     m_parent_ctxt (parent_ctxt),
     m_toplevel_ctxt (m_parent_ctxt ? m_parent_ctxt->m_toplevel_ctxt : this),
+    m_timer (NULL),
     m_error_count (0),
     m_first_error_str (NULL),
     m_owns_first_error_str (false),
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 439e7ce..19969d5 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -24,6 +24,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-common.h"
 #include "jit-logging.h"
 
+class timer;
+
 namespace gcc {
 
 namespace jit {
@@ -255,6 +257,9 @@ public:
   void
   get_all_requested_dumps (vec <recording::requested_dump> *out);
 
+  void set_timer (timer *t) { m_timer = t; }
+  timer *get_timer () const { return m_timer; }
+
 private:
   void log_all_options () const;
   void log_str_option (enum gcc_jit_str_option opt) const;
@@ -270,6 +275,8 @@ private:
      contexts.  This has itself as its own m_toplevel_ctxt.  */
   context *m_toplevel_ctxt;
 
+  timer *m_timer;
+
   int m_error_count;
 
   char *m_first_error_str;
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 62ef6a4..c100eb4 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -45,6 +45,8 @@ namespace gccjit
     class rvalue;
      class lvalue;
        class param;
+  class timer;
+  class auto_time;
 
   /* Errors within the API become C++ exceptions of this class.  */
   class error
@@ -120,6 +122,9 @@ namespace gccjit
     void set_bool_option (enum gcc_jit_bool_option opt,
   int value);
 
+    void set_timer (gccjit::timer t);
+    gccjit::timer get_timer () const;
+
     location
     new_location (const std::string &filename,
   int line,
@@ -502,6 +507,35 @@ namespace gccjit
 
   /* Dereferencing. */
   lvalue operator* (rvalue ptr);
+
+  class timer
+  {
+  public:
+    timer ();
+    timer (gcc_jit_timer *inner_timer);
+
+    void push (const char *item_name);
+    void pop ();
+    void print (FILE *f_out) const;
+
+    void release ();
+
+    gcc_jit_timer *get_inner_timer () const;
+
+  private:
+    gcc_jit_timer *m_inner_timer;
+  };
+
+  class auto_time
+  {
+  public:
+    auto_time (timer t, const char *item_name);
+    auto_time (context ctxt, const char *item_name);
+    ~auto_time ();
+
+  private:
+    timer m_timer;
+  };
 }
 
 /****************************************************************************
@@ -603,6 +637,19 @@ context::set_bool_option (enum gcc_jit_bool_option opt,
 
 }
 
+inline void
+context::set_timer (gccjit::timer t)
+{
+  gcc_jit_context_set_timer (m_inner_ctxt, t.get_inner_timer ());
+}
+
+inline gccjit::timer
+context::get_timer () const
+{
+  return gccjit::timer (gcc_jit_context_get_timer (m_inner_ctxt));
+}
+
+
 inline location
 context::new_location (const std::string &filename,
        int line,
@@ -1634,6 +1681,73 @@ inline lvalue operator* (rvalue ptr)
   return ptr.dereference ();
 }
 
+// class timer
+inline
+timer::timer ()
+{
+  m_inner_timer = gcc_jit_timer_new ();
+}
+
+inline
+timer::timer (gcc_jit_timer *inner_timer)
+{
+  m_inner_timer = inner_timer;
+}
+
+inline void
+timer::push (const char *item_name)
+{
+  gcc_jit_timer_push (m_inner_timer, item_name);
+
+}
+
+inline void
+timer::pop ()
+{
+  gcc_jit_timer_pop (m_inner_timer);
+}
+
+inline void
+timer::print (FILE *f_out) const
+{
+  gcc_jit_timer_print (m_inner_timer, f_out);
+}
+
+inline gcc_jit_timer *
+timer::get_inner_timer () const
+{
+  return m_inner_timer;
+}
+
+inline void
+timer::release ()
+{
+  gcc_jit_timer_release (m_inner_timer);
+  m_inner_timer = NULL;
+}
+
+// class auto_time
+
+inline
+auto_time::auto_time (timer t, const char *item_name)
+  : m_timer (t)
+{
+  t.push (item_name);
+}
+
+inline
+auto_time::auto_time (context ctxt, const char *item_name)
+  : m_timer (ctxt.get_timer ())
+{
+  m_timer.push (item_name);
+}
+
+inline
+auto_time::~auto_time ()
+{
+  m_timer.pop ();
+}
+
 } // namespace gccjit
 
 #endif /* #ifndef LIBGCCJIT_PLUS_PLUS_H */
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 7eb66bd..2a67ef7 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "opts.h"
 #include "safe-ctype.h"
+#include "timevar.h"
 
 #include "libgccjit.h"
 #include "jit-common.h"
@@ -84,6 +85,10 @@ struct gcc_jit_param : public gcc::jit::recording::param
 {
 };
 
+struct gcc_jit_timer : public timer
+{
+};
+
 /**********************************************************************
  Error-handling.
 
@@ -2370,3 +2375,88 @@ gcc_jit_result_release (gcc_jit_result *result)
   result->log ("deleting result: %p", (void *)result);
   delete result;
 }
+
+/**********************************************************************
+ Timing support.
+ **********************************************************************/
+
+/* Create a gcc_jit_timer instance, and start timing.  */
+
+gcc_jit_timer *
+gcc_jit_timer_new (void)
+{
+  gcc_jit_timer *timer = new gcc_jit_timer ();
+  timer->start (TV_TOTAL);
+  timer->push (TV_JIT_CLIENT_CODE);
+  return timer;
+}
+
+/* Release a gcc_jit_timer instance.  */
+
+void
+gcc_jit_timer_release (gcc_jit_timer *timer)
+{
+  RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
+
+  delete timer;
+}
+
+/* Associate a gcc_jit_timer instance with a context.  */
+
+void
+gcc_jit_context_set_timer (gcc_jit_context *ctxt,
+   gcc_jit_timer *timer)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
+  RETURN_IF_FAIL (timer, ctxt, NULL, "NULL timer");
+
+  ctxt->set_timer (timer);
+}
+
+/* Get the timer associated with a context (if any).  */
+
+gcc_jit_timer *
+gcc_jit_context_get_timer (gcc_jit_context *ctxt)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
+
+  return (gcc_jit_timer *)ctxt->get_timer ();
+}
+
+/* Push the given item onto the timing stack.  */
+
+void
+gcc_jit_timer_push (gcc_jit_timer *timer,
+    const char *item_name)
+{
+  RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
+  RETURN_IF_FAIL (item_name, NULL, NULL, "NULL item_name");
+  timer->push_client_item (item_name);
+}
+
+/* Pop the top item from the timing stack.  */
+
+void
+gcc_jit_timer_pop (gcc_jit_timer *timer)
+{
+  RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
+  /* FIXME: mismatching item? */
+  timer->pop_client_item ();
+}
+
+/* Print timing information to the given stream about activity since
+   the timer was started.  */
+
+void
+gcc_jit_timer_print (gcc_jit_timer *timer,
+     FILE *f_out)
+{
+  RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
+  RETURN_IF_FAIL (f_out, NULL, NULL, "NULL f_out");
+
+  timer->pop (TV_JIT_CLIENT_CODE);
+  timer->stop (TV_TOTAL);
+  timer->print (f_out);
+  timer->start (TV_TOTAL);
+  timer->push (TV_JIT_CLIENT_CODE);
+}
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 8f4354e..cfae293 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1133,6 +1133,57 @@ gcc_jit_context_enable_dump (gcc_jit_context *ctxt,
      const char *dumpname,
      char **out_ptr);
 
+/**********************************************************************
+ Timing support.
+ **********************************************************************/
+
+/* The timer API was added to libgccjit in GCC 6.
+   This macro is only defined in versions of libgccjit.h which have the
+   timer API, and so can be used to guard code that may need to compile
+   against earlier releases.*/
+#define LIBGCCJIT_HAS_FEATURE_gcc_jit_timer
+
+typedef struct gcc_jit_timer gcc_jit_timer;
+
+/* Create a gcc_jit_timer instance, and start timing.  */
+
+extern gcc_jit_timer *
+gcc_jit_timer_new (void);
+
+/* Release a gcc_jit_timer instance.  */
+
+extern void
+gcc_jit_timer_release (gcc_jit_timer *timer);
+
+/* Associate a gcc_jit_timer instance with a context.  */
+
+extern void
+gcc_jit_context_set_timer (gcc_jit_context *ctxt,
+   gcc_jit_timer *timer);
+
+/* Get the timer associated with a context (if any).  */
+
+extern gcc_jit_timer *
+gcc_jit_context_get_timer (gcc_jit_context *ctxt);
+
+/* Push the given item onto the timing stack.  */
+
+extern void
+gcc_jit_timer_push (gcc_jit_timer *timer,
+    const char *item_name);
+
+/* Pop the top item from the timing stack.  */
+
+extern void
+gcc_jit_timer_pop (gcc_jit_timer *timer);
+
+/* Print timing information to the given stream about activity since
+   the timer was started.  */
+
+extern void
+gcc_jit_timer_print (gcc_jit_timer *timer,
+     FILE *f_out);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 89bd57be4..1733b94 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -39,6 +39,7 @@
     gcc_jit_context_get_builtin_function;
     gcc_jit_context_get_first_error;
     gcc_jit_context_get_last_error;
+    gcc_jit_context_get_timer;
     gcc_jit_context_get_type;
     gcc_jit_context_get_int_type;
     gcc_jit_context_new_array_access;
@@ -71,6 +72,7 @@
     gcc_jit_context_set_int_option;
     gcc_jit_context_set_logfile;
     gcc_jit_context_set_str_option;
+    gcc_jit_context_set_timer;
     gcc_jit_context_zero;
     gcc_jit_field_as_object;
     gcc_jit_function_as_object;
@@ -98,6 +100,11 @@
     gcc_jit_rvalue_get_type;
     gcc_jit_struct_as_type;
     gcc_jit_struct_set_fields;
+    gcc_jit_timer_new;
+    gcc_jit_timer_release;
+    gcc_jit_timer_push;
+    gcc_jit_timer_pop;
+    gcc_jit_timer_print;
     gcc_jit_type_as_object;
     gcc_jit_type_get_const;
     gcc_jit_type_get_pointer;
diff --git a/gcc/main.c b/gcc/main.c
index 79baf0d..bbd8b67 100644
--- a/gcc/main.c
+++ b/gcc/main.c
@@ -33,7 +33,7 @@ int main (int argc, char **argv);
 int
 main (int argc, char **argv)
 {
-  toplev toplev (true, /* use_TV_TOTAL */
+  toplev toplev (NULL, /* external_timer */
  true /* init_signals */);
 
   return toplev.main (argc, argv);
diff --git a/gcc/testsuite/jit.dg/test-benchmark.c b/gcc/testsuite/jit.dg/test-benchmark.c
index 324ba93..7e330de 100644
--- a/gcc/testsuite/jit.dg/test-benchmark.c
+++ b/gcc/testsuite/jit.dg/test-benchmark.c
@@ -141,11 +141,13 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
 
 /* Run one iteration of the test.  */
 static void
-test_jit (const char *argv0, int opt_level)
+test_jit (const char *argv0, int opt_level, gcc_jit_timer *timer)
 {
   gcc_jit_context *ctxt;
   gcc_jit_result *result;
 
+  gcc_jit_timer_push (timer, "test_jit");
+
   ctxt = gcc_jit_context_acquire ();
   if (!ctxt)
     {
@@ -153,6 +155,8 @@ test_jit (const char *argv0, int opt_level)
       return;
     }
 
+  gcc_jit_context_set_timer (ctxt, timer);
+
   /* Set up options.  */
   gcc_jit_context_set_str_option (
     ctxt,
@@ -182,13 +186,22 @@ test_jit (const char *argv0, int opt_level)
       GCC_JIT_BOOL_OPTION_DUMP_SUMMARY,
       1);
 
+  gcc_jit_timer_push (timer, "create_code");
   create_code (ctxt, NULL);
+  gcc_jit_timer_pop (timer);
 
+  gcc_jit_timer_push (timer, "compile");
   result = gcc_jit_context_compile (ctxt);
+  gcc_jit_timer_pop (timer);
+
+  gcc_jit_timer_push (timer, "verify_code");
   verify_code (ctxt, result);
+  gcc_jit_timer_pop (timer);
 
   gcc_jit_context_release (ctxt);
   gcc_jit_result_release (result);
+
+  gcc_jit_timer_pop (timer);
 }
 
 /* Taken from timevar.c.  */
@@ -217,16 +230,19 @@ main (int argc, char **argv)
       int i;
       double start_time, end_time, elapsed_time;
       start_time = get_wallclock_time ();
+      gcc_jit_timer *timer = gcc_jit_timer_new ();
       for (i = 1; i <= num_iterations; i++)
  {
   snprintf (test, sizeof (test),
     "%s iteration %d of %d",
     extract_progname (argv[0]),
     i, num_iterations);
-  test_jit (argv[0], opt_level);
+  test_jit (argv[0], opt_level, timer);
  }
       end_time = get_wallclock_time ();
       elapsed_time = end_time - start_time;
+      gcc_jit_timer_print (timer, stderr);
+      gcc_jit_timer_release (timer);
       pass ("%s: survived %i iterations at optlevel %i",
     argv[0], num_iterations, opt_level);
       note (("%s: %i iterations at optlevel %i"
diff --git a/gcc/timevar.c b/gcc/timevar.c
index 76ad22a..9bc62e6 100644
--- a/gcc/timevar.c
+++ b/gcc/timevar.c
@@ -20,7 +20,10 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
 #include "timevar.h"
+#include "hash-map.h"
+#include "vec.h"
 
 #ifndef HAVE_CLOCK_T
 typedef int clock_t;
@@ -120,6 +123,93 @@ static void timevar_accumulate (struct timevar_time_def *,
  struct timevar_time_def *,
  struct timevar_time_def *);
 
+/* The implementation of timing events for jit client code, allowing
+   arbitrary named items to appear on the timing stack.  */
+
+class timer::named_items
+{
+ public:
+  named_items (timer *t);
+  ~named_items ();
+
+  void push (const char *item_name);
+  void pop ();
+  void print (FILE *fp, const timevar_time_def *total);
+
+ private:
+  /* Which timer instance does this relate to?  */
+  timer *m_timer;
+
+  /* Dictionary, mapping from item names to timevar_def.
+     Note that currently we merely store/compare the raw string
+     pointers provided by client code; we don't take a copy,
+     or use strcmp.  */
+  hash_map <const char *, timer::timevar_def> m_hash_map;
+
+  /* The order in which items were originally inserted.  */
+  auto_vec <const char *> m_names;
+};
+
+/* The constructor for class timer::named_items.  */
+
+timer::named_items::named_items (timer *t)
+: m_timer (t),
+  m_hash_map (),
+  m_names ()
+{
+}
+
+/* The destructor for class timer::named_items.  */
+
+timer::named_items::~named_items ()
+{
+}
+
+/* Push the named item onto the timer stack.  */
+
+void
+timer::named_items::push (const char *item_name)
+{
+  gcc_assert (item_name);
+
+  bool existed;
+  timer::timevar_def *def = &m_hash_map.get_or_insert (item_name, &existed);
+  if (!existed)
+    {
+      def->elapsed.user = 0;
+      def->elapsed.sys = 0;
+      def->elapsed.wall = 0;
+      def->name = item_name;
+      def->standalone = 0;
+      m_names.safe_push (item_name);
+    }
+  m_timer->push_internal (def);
+}
+
+/* Pop the top item from the timer stack.  */
+
+void
+timer::named_items::pop ()
+{
+  m_timer->pop_internal ();
+}
+
+/* Print the given client item.  Helper function for timer::print.  */
+
+void
+timer::named_items::print (FILE *fp, const timevar_time_def *total)
+{
+  unsigned int i;
+  const char *item_name;
+  fprintf (fp, "Client items:\n");
+  FOR_EACH_VEC_ELT (m_names, i, item_name)
+    {
+      timer::timevar_def *def = m_hash_map.get (item_name);
+      gcc_assert (def);
+      m_timer->print_row (fp, total, def);
+    }
+}
+
 /* Fill the current times into TIME.  The definition of this function
    also defines any or all of the HAVE_USER_TIME, HAVE_SYS_TIME, and
    HAVE_WALL_TIME macros.  */
@@ -169,7 +259,8 @@ timevar_accumulate (struct timevar_time_def *timer,
 timer::timer () :
   m_stack (NULL),
   m_unused_stack_instances (NULL),
-  m_start_time ()
+  m_start_time (),
+  m_jit_client_items (NULL)
 {
   /* Zero all elapsed times.  */
   memset (m_timevars, 0, sizeof (m_timevars));
@@ -190,6 +281,26 @@ timer::timer () :
 #endif
 }
 
+/* Class timer's destructor.  */
+
+timer::~timer ()
+{
+  timevar_stack_def *iter, *next;
+
+  for (iter = m_stack; iter; iter=next)
+    {
+      next = iter->next;
+      free (iter);
+    }
+  for (iter = m_unused_stack_instances; iter; iter=next)
+    {
+      next = iter->next;
+      free (iter);
+    }
+
+  delete m_jit_client_items;
+}
+
 /* Initialize timing variables.  */
 
 void
@@ -212,9 +323,20 @@ void
 timer::push (timevar_id_t timevar)
 {
   struct timevar_def *tv = &m_timevars[timevar];
+  push_internal (tv);
+}
+
+/* Push TV onto the timing stack, either one of the builtin ones
+   for a timevar_id_t, or one provided by client code to libgccjit.  */
+
+void
+timer::push_internal (struct timevar_def *tv)
+{
   struct timevar_stack_def *context;
   struct timevar_time_def now;
 
+  gcc_assert (tv);
+
   /* Mark this timing variable as used.  */
   tv->used = 1;
 
@@ -258,11 +380,20 @@ timer::push (timevar_id_t timevar)
 void
 timer::pop (timevar_id_t timevar)
 {
+  gcc_assert (&m_timevars[timevar] == m_stack->timevar);
+
+  pop_internal ();
+}
+
+/* Pop the topmost item from the stack, either one of the builtin ones
+   for a timevar_id_t, or one provided by client code to libgccjit.  */
+
+void
+timer::pop_internal ()
+{
   struct timevar_time_def now;
   struct timevar_stack_def *popped = m_stack;
 
-  gcc_assert (&m_timevars[timevar] == m_stack->timevar);
-
   /* What time is it?  */
   get_time (&now);
 
@@ -410,6 +541,28 @@ timer::cond_stop (timevar_id_t timevar)
   timevar_accumulate (&tv->elapsed, &tv->start_time, &now);
 }
 
+/* Push the named item onto the timing stack.  */
+
+void
+timer::push_client_item (const char *item_name)
+{
+  gcc_assert (item_name);
+
+  /* Lazily create the named_items instance.  */
+  if (!m_jit_client_items)
+    m_jit_client_items = new named_items (this);
+
+  m_jit_client_items->push (item_name);
+}
+
+/* Pop the top-most client item from the timing stack.  */
+
+void
+timer::pop_client_item ()
+{
+  gcc_assert (m_jit_client_items);
+  m_jit_client_items->pop ();
+}
 
 /* Validate that phase times are consistent.  */
 
@@ -462,6 +615,46 @@ timer::validate_phases (FILE *fp) const
     }
 }
 
+/* Helper function for timer::print.  */
+
+void
+timer::print_row (FILE *fp,
+  const timevar_time_def *total,
+  const timevar_def *tv)
+{
+  /* The timing variable name.  */
+  fprintf (fp, " %-24s:", tv->name);
+
+#ifdef HAVE_USER_TIME
+  /* Print user-mode time for this process.  */
+  fprintf (fp, "%7.2f (%2.0f%%) usr",
+   tv->elapsed.user,
+   (total->user == 0 ? 0 : tv->elapsed.user / total->user) * 100);
+#endif /* HAVE_USER_TIME */
+
+#ifdef HAVE_SYS_TIME
+  /* Print system-mode time for this process.  */
+  fprintf (fp, "%7.2f (%2.0f%%) sys",
+   tv->elapsed.sys,
+   (total->sys == 0 ? 0 : tv->elapsed.sys / total->sys) * 100);
+#endif /* HAVE_SYS_TIME */
+
+#ifdef HAVE_WALL_TIME
+  /* Print wall clock time elapsed.  */
+  fprintf (fp, "%7.2f (%2.0f%%) wall",
+   tv->elapsed.wall,
+   (total->wall == 0 ? 0 : tv->elapsed.wall / total->wall) * 100);
+#endif /* HAVE_WALL_TIME */
+
+  /* Print the amount of ggc memory allocated.  */
+  fprintf (fp, "%8u kB (%2.0f%%) ggc",
+   (unsigned) (tv->elapsed.ggc_mem >> 10),
+   (total->ggc_mem == 0
+    ? 0
+    : (float) tv->elapsed.ggc_mem / total->ggc_mem) * 100);
+
+  putc ('\n', fp);
+}
 
 /* Summarize timing variables to FP.  The timing variable TV_TOTAL has
    a special meaning -- it's considered to be the total elapsed time,
@@ -494,6 +687,8 @@ timer::print (FILE *fp)
   m_start_time = now;
 
   fputs ("\nExecution times (seconds)\n", fp);
+  if (m_jit_client_items)
+    fputs ("GCC items:\n", fp);
   for (id = 0; id < (unsigned int) TIMEVAR_LAST; ++id)
     {
       const timevar_def *tv = &m_timevars[(timevar_id_t) id];
@@ -516,39 +711,10 @@ timer::print (FILE *fp)
   && tv->elapsed.ggc_mem < GGC_MEM_BOUND)
  continue;
 
-      /* The timing variable name.  */
-      fprintf (fp, " %-24s:", tv->name);
-
-#ifdef HAVE_USER_TIME
-      /* Print user-mode time for this process.  */
-      fprintf (fp, "%7.2f (%2.0f%%) usr",
-       tv->elapsed.user,
-       (total->user == 0 ? 0 : tv->elapsed.user / total->user) * 100);
-#endif /* HAVE_USER_TIME */
-
-#ifdef HAVE_SYS_TIME
-      /* Print system-mode time for this process.  */
-      fprintf (fp, "%7.2f (%2.0f%%) sys",
-       tv->elapsed.sys,
-       (total->sys == 0 ? 0 : tv->elapsed.sys / total->sys) * 100);
-#endif /* HAVE_SYS_TIME */
-
-#ifdef HAVE_WALL_TIME
-      /* Print wall clock time elapsed.  */
-      fprintf (fp, "%7.2f (%2.0f%%) wall",
-       tv->elapsed.wall,
-       (total->wall == 0 ? 0 : tv->elapsed.wall / total->wall) * 100);
-#endif /* HAVE_WALL_TIME */
-
-      /* Print the amount of ggc memory allocated.  */
-      fprintf (fp, "%8u kB (%2.0f%%) ggc",
-       (unsigned) (tv->elapsed.ggc_mem >> 10),
-       (total->ggc_mem == 0
- ? 0
- : (float) tv->elapsed.ggc_mem / total->ggc_mem) * 100);
-
-      putc ('\n', fp);
+      print_row (fp, total, tv);
     }
+  if (m_jit_client_items)
+    m_jit_client_items->print (fp, total);
 
   /* Print total time.  */
   fputs (" TOTAL                 :", fp);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index cf8f37d..905b469 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -289,3 +289,5 @@ DEFTIMEVAR (TV_JIT_REPLAY     , "replay of JIT client activity")
 DEFTIMEVAR (TV_ASSEMBLE     , "assemble JIT code")
 DEFTIMEVAR (TV_LINK     , "link JIT code")
 DEFTIMEVAR (TV_LOAD     , "load JIT result")
+DEFTIMEVAR (TV_JIT_ACQUIRING_MUTEX   , "acquiring JIT mutex")
+DEFTIMEVAR (TV_JIT_CLIENT_CODE   , "JIT client code")
diff --git a/gcc/timevar.h b/gcc/timevar.h
index 71a814a..d46dc88 100644
--- a/gcc/timevar.h
+++ b/gcc/timevar.h
@@ -115,12 +115,22 @@ 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 print (FILE *fp);
 
  private:
   /* Private member functions.  */
   void validate_phases (FILE *fp) const;
 
+  struct timevar_def;
+  void push_internal (struct timevar_def *tv);
+  void pop_internal ();
+  static void print_row (FILE *fp,
+ const timevar_time_def *total,
+ const timevar_def *tv);
+
  private:
 
   /* Private type: a timing variable.  */
@@ -157,6 +167,12 @@ class timer
     struct timevar_stack_def *next;
   };
 
+  /* 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:
 
   /* Data members (all private).  */
@@ -176,6 +192,11 @@ class timer
      pushed.  Time elapsed since then is attributed to the topmost
      element.  */
   timevar_time_def m_start_time;
+
+  /* If non-NULL, for use when timing libgccjit's client code.  */
+  named_items *m_jit_client_items;
+
+  friend class named_items;
 };
 
 /* Provided for backward compatibility.  */
@@ -198,15 +219,18 @@ timevar_pop (timevar_id_t tv)
 class auto_timevar
 {
  public:
-  auto_timevar (timevar_id_t tv)
-    : m_tv (tv)
+  auto_timevar (timer *t, timevar_id_t tv)
+    : m_timer (t),
+      m_tv (tv)
   {
-    timevar_push (m_tv);
+    if (m_timer)
+      m_timer->push (m_tv);
   }
 
   ~auto_timevar ()
   {
-    timevar_pop (m_tv);
+    if (m_timer)
+      m_timer->pop (m_tv);
   }
 
  private:
@@ -214,6 +238,7 @@ class auto_timevar
   // Private to disallow copies.
   auto_timevar (const auto_timevar &);
 
+  timer *m_timer;
   timevar_id_t m_tv;
 };
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 54eefcd..3527b5d 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1818,7 +1818,7 @@ static int rtl_initialized;
 void
 initialize_rtl (void)
 {
-  auto_timevar tv (TV_INITIALIZE_RTL);
+  auto_timevar tv (g_timer, TV_INITIALIZE_RTL);
 
   /* Initialization done just once per compilation, but delayed
      till code generation.  */
@@ -2090,23 +2090,29 @@ do_compile ()
     }
 }
 
-toplev::toplev (bool use_TV_TOTAL, bool init_signals)
-  : m_use_TV_TOTAL (use_TV_TOTAL),
+toplev::toplev (timer *external_timer,
+ bool init_signals)
+  : m_use_TV_TOTAL (external_timer == NULL),
     m_init_signals (init_signals)
 {
-  if (!m_use_TV_TOTAL)
-    start_timevars ();
+  if (external_timer)
+    g_timer = external_timer;
 }
 
 toplev::~toplev ()
 {
-  if (g_timer)
+  if (g_timer && m_use_TV_TOTAL)
     {
       g_timer->stop (TV_TOTAL);
       g_timer->print (stderr);
+      delete g_timer;
+      g_timer = NULL;
     }
 }
 
+/* Potentially call timevar_init (which will create g_timevars if it
+   doesn't already exist).  */
+
 void
 toplev::start_timevars ()
 {
diff --git a/gcc/toplev.h b/gcc/toplev.h
index 9527f76..4b3748b 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -24,11 +24,13 @@ along with GCC; see the file COPYING3.  If not see
 extern struct cl_decoded_option *save_decoded_options;
 extern unsigned int save_decoded_options_count;
 
+class timer;
+
 /* Invoking the compiler.  */
 class toplev
 {
 public:
-  toplev (bool use_TV_TOTAL,
+  toplev (timer *external_timer,
   bool init_signals);
   ~toplev ();
 
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

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

David Malcolm
In reply to this post by David Malcolm
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

Reply | Threaded
Open this post in threaded view
|

[PATCH 10/16] binutils: Introduce "gas_main", and state-purging with "gas" subdir

David Malcolm
In reply to this post by David Malcolm
Currently this has only been tested on x86_64, and hardcodes this
cleanup:
  extern void config_obj_elf_c_finalize (void);
  extern void config_tc_i386_c_finalize (void);
Clearly this would need generalizing.

gas/ChangeLog:
        * app.c (app_c_finalize): New function.
        * as.c: Include new header "libgas.h".
        (gas_timer): New variable.
        (emulations): Make the underlying struct emulation be const.
        (parse_args): Reset internal state of getopt_long_only.
        (main): Rename to gas_internal_main.  Make static.
        Add param "standalone".  Add uses of GAS_TIMER_PUSH and
        GAS_TIMER_POP.  Conditionalize calls to xatexit with check
        for "standalone", and explicitly call close_output_file in
        the non-standalone case.  Replace calls to xexit with
        return statements.
        (config_obj_elf_c_finalize): Add prototype.
        (config_tc_i386_c_finalize): Likwise.
        (gas_main): New function.
        (as_c_finalize): New function.
        * as.h (app_c_finalize): New prototype.
        (depend_c_finalize): New prototype.
        (gas_timer): New variable.
        (GAS_TIMER_PUSH): New macro.
        (GAS_TIMER_POP): New macro.
        * asmain.c: New file.
        * config/obj-elf.c (obj_elf_ident): Split out the function-level
        static variable "comment_section" into...
        (obj_elf_ident_comment_section): ...this file-level static
        variable.
        (config_obj_elf_c_finalize): New function.
        * config/tc-i386.c (config_tc_i386_c_finalize): New function.
        * depend.c (depend_c_finalize): New function.
        * dw2gencfi.c (dw2gencfi_c_finalize): New function.
        * dw2gencfi.h (dw2gencfi_c_finalize): New prototype.
        * libgas.h: New file.
        * macro.c (macro_c_finalize): New function.
        * macro.h (macro_c_finalize): New prototype.
        * output-file.c (output_file_c_finalize): New function.
        * output-file.h (output_file_c_finalize): New prototype.
        * read.c (read_c_finalize): New function.
        * read.h (read_c_finalize): New prototype.
        * subsegs.c (subsegs_c_finalize): New function.
        * subsegs.h (subsegs_c_finalize): New prototype.
        * symbols.c (symbols_c_finalize): New function.
        * symbols.h (symbols_c_finalize): New prototype.
        * write.c (write_c_finalize): New function.
        * write.h (write_c_finalize): New prototype.
---
 gas/app.c            |  27 ++++++++
 gas/as.c             | 182 ++++++++++++++++++++++++++++++++++++++++++++++++---
 gas/as.h             |  12 ++++
 gas/asmain.c         |  35 ++++++++++
 gas/config/obj-elf.c |  25 +++++--
 gas/config/tc-i386.c |  79 ++++++++++++++++++++++
 gas/depend.c         |  10 +++
 gas/dw2gencfi.c      |  12 ++++
 gas/dw2gencfi.h      |   2 +
 gas/libgas.h         |  41 ++++++++++++
 gas/macro.c          |  18 +++++
 gas/macro.h          |   2 +
 gas/output-file.c    |   8 +++
 gas/output-file.h    |   1 +
 gas/read.c           |  47 +++++++++++++
 gas/read.h           |   2 +
 gas/subsegs.c        |  10 +++
 gas/subsegs.h        |   1 +
 gas/symbols.c        |  41 ++++++++++++
 gas/symbols.h        |   2 +
 gas/write.c          |  15 +++++
 gas/write.h          |   1 +
 22 files changed, 558 insertions(+), 15 deletions(-)
 create mode 100644 gas/asmain.c
 create mode 100644 gas/libgas.h

diff --git a/gas/app.c b/gas/app.c
index 67402e2..fda8216 100644
--- a/gas/app.c
+++ b/gas/app.c
@@ -1479,3 +1479,30 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen)
 
   return to - tostart;
 }
+
+/* Reset all global state within app.c.  */
+
+void
+app_c_finalize (void)
+{
+#ifdef H_TICK_HEX
+  enable_h_tick_hex = 0;
+#endif
+#ifdef TC_M68K
+  scrub_m68k_mri = 0;
+#endif
+#if defined TC_ARM && defined OBJ_ELF
+  symver_state = NULL;
+#endif
+  CLEAR_VAR (lex);
+  state = 0;
+  old_state = 0;
+  out_string = NULL;
+  CLEAR_VAR (out_buf);
+  add_newlines = 0;
+  saved_input = NULL;
+  saved_input_len = 0;
+  CLEAR_VAR (input_buffer);
+  mri_state = NULL;
+  mri_last_ch = 0;
+}
diff --git a/gas/as.c b/gas/as.c
index 2a8923f..90dc298 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -31,6 +31,8 @@
 
 #define COMMON
 
+#include "libgas.h"
+
 #include "as.h"
 #include "subsegs.h"
 #include "output-file.h"
@@ -66,6 +68,8 @@ struct defsym_list
   valueT value;
 };
 
+/* Global timer.  */
+struct ctimer *gas_timer;
 
 /* True if a listing is wanted.  */
 int listing;
@@ -135,7 +139,7 @@ extern struct emulation mipsbelf, mipslelf, mipself;
 extern struct emulation i386coff, i386elf, i386aout;
 extern struct emulation crisaout, criself;
 
-static struct emulation *const emulations[] = { EMULATIONS };
+static const struct emulation *const emulations[] = { EMULATIONS };
 static const int n_emulations = sizeof (emulations) / sizeof (emulations[0]);
 
 static void
@@ -546,6 +550,9 @@ parse_args (int * pargc, char *** pargv)
   new_argc = 1;
   new_argv[new_argc] = NULL;
 
+  /* Reset internal state of getopt_long_only.  */
+  optind = 0;
+
   while (1)
     {
       /* getopt_long_only is like getopt_long, but '-' as well as '--' can
@@ -1122,13 +1129,18 @@ perform_an_assembly_pass (int argc, char ** argv)
 }
 
 
-int
-main (int argc, char ** argv)
+/* Run the assembler: almost the top-level of the assembler implementation.
+   Called by gas_main after it has set up gas_timer.  */
+
+static int
+gas_internal_main (int argc, char ** argv, int standalone)
 {
   char ** argv_orig = argv;
 
   int macro_strip_at;
 
+  GAS_TIMER_PUSH ("before pass");
+
   start_time = get_run_time ();
 #ifdef HAVE_SBRK
   start_sbrk = (char *) sbrk (0);
@@ -1183,9 +1195,10 @@ main (int argc, char ** argv)
   expr_begin ();
 
   /* It has to be called after dump_statistics ().  */
-  xatexit (close_output_file);
+  if (standalone)
+    xatexit (close_output_file);
 
-  if (flag_print_statistics)
+  if (flag_print_statistics && standalone)
     xatexit (dump_statistics);
 
   macro_strip_at = 0;
@@ -1236,8 +1249,14 @@ main (int argc, char ** argv)
 
   PROGRESS (1);
 
+  GAS_TIMER_POP ();
+
   /* Assemble it.  */
+  GAS_TIMER_PUSH ("perform_an_assembly_pass");
   perform_an_assembly_pass (argc, argv);
+  GAS_TIMER_POP ();
+
+  GAS_TIMER_PUSH ("after pass");
 
   cond_finish_check (-1);
 
@@ -1316,13 +1335,158 @@ main (int argc, char ** argv)
 
   END_PROGRESS (myname);
 
-  /* Use xexit instead of return, because under VMS environments they
-     may not place the same interpretation on the value given.  */
+  if (!standalone)
+    close_output_file ();
+
+  GAS_TIMER_POP ();
+
   if (had_errors () != 0)
-    xexit (EXIT_FAILURE);
+    return EXIT_FAILURE;
 
   /* Only generate dependency file if assembler was successful.  */
   print_dependencies ();
 
-  xexit (EXIT_SUCCESS);
+  return EXIT_SUCCESS;
+}
+
+static void as_c_finalize (void);
+
+/* FIXME. */
+extern void config_obj_elf_c_finalize (void);
+extern void config_tc_i386_c_finalize (void);
+
+/* Public API entrypoint. */
+
+int
+gas_main (int argc, char **argv, int standalone, struct ctimer *timer)
+{
+  int result;
+
+  /* Set global gas_timer.  */
+  gas_timer = timer;
+
+  GAS_TIMER_PUSH ("gas_main");
+
+  result = gas_internal_main (argc, argv, standalone);
+  if (!standalone)
+    {
+      GAS_TIMER_PUSH ("cleanup");
+
+      if (flag_print_statistics)
+        dump_statistics ();
+
+      /* Restore all state.  */
+      bfd_uninit ();
+
+      /* Restore all state within gas.  */
+      app_c_finalize ();
+      as_c_finalize ();
+      depend_c_finalize ();
+      dw2gencfi_c_finalize ();
+      macro_c_finalize ();
+      read_c_finalize ();
+      output_file_c_finalize ();
+      subsegs_c_finalize ();
+      symbols_c_finalize ();
+      write_c_finalize ();
+      /* FIXME: */
+      config_obj_elf_c_finalize ();
+      config_tc_i386_c_finalize ();
+
+      GAS_TIMER_POP ();
+    }
+
+  GAS_TIMER_POP ();
+
+  gas_timer = NULL;
+
+  return result;
+}
+
+/* Reset all global state within as.c.  */
+
+static void
+as_c_finalize (void)
+{
+  listing = 0;
+  debug_type = DEBUG_UNSPECIFIED;
+  use_gnu_debug_info_extensions = 0;
+  max_macro_nest = 100;
+  myname = NULL;
+  chunksize = 0;
+  debug_memory = 0;
+  verbose = 0;
+  keep_it = 0;
+  reg_section = NULL;
+  expr_section = NULL;
+  text_section = NULL;
+  data_section = NULL;
+  bss_section = NULL;
+  listing_filename = NULL;
+  defsyms = NULL;
+#ifdef HAVE_ITBL_CPU
+  itbl_files = NULL;
+#endif
+  start_time = 0;
+#ifdef HAVE_SBRK
+  start_sbrk = NULL;
+#endif
+  flag_macro_alternate = 0;
+
+  /* The header files use the prefix COMMON for various declarations,
+     which for most translation units is "extern".
+     Within as.c, COMMON is empty, and so this file contains
+     their definitions.
+     Reset their state here.  */
+
+  /* "COMMON" vars within as.h.  */
+  now_subseg = 0;
+  now_seg = NULL;
+  flag_no_comments = 0;
+  flag_debug = 0;
+  flag_signed_overflow_ok = 0;
+#ifndef WORKING_DOT_WORD
+  flag_warn_displacement = 0;
+#endif
+  flag_keep_locals = 0;
+  flag_mri = 0;
+  flag_readonly_data_in_text = 0;
+  flag_no_warnings = 0;
+  flag_fatal_warnings = 0;
+  flag_always_generate_output = 0;
+  flag_print_statistics = 0;
+  flag_strip_local_absolute = 0;
+  flag_traditional_format = 0;
+  flag_compress_debug = 0;
+  flag_execstack = 0;
+  flag_noexecstack = 0;
+  out_file_name = NULL;
+  insttbl_file_name = NULL;
+  need_pass_2 = 0;
+  linkrelax = 0;
+  flag_dwarf_sections = 0;
+#ifdef TC_M68K
+  flag_m68k_mri = 0;
+#endif
+#ifdef WARN_COMMENTS
+  warn_comment = 0;
+  found_comment = 0;
+  found_comment_file = NULL;
+#endif
+#if defined OBJ_ELF || defined OBJ_MAYBE_ELF
+  flag_size_check = 0;
+#endif
+
+  /* "COMMON" var within emul.h.  */
+#ifdef USE_EMULATIONS
+  this_emulation = NULL;
+#endif
+
+  /* "COMMON" vars within frags.h.  */
+  frag_now = NULL;
+  CLEAR_VAR (zero_address_frag);
+  CLEAR_VAR (predefined_address_frag);
+
+  /* "COMMON" var within obj.h.  */
+  this_format = NULL;
 }
diff --git a/gas/as.h b/gas/as.h
index 6de319e..8e241a9 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -517,6 +517,9 @@ void   register_dependency (char *);
 void   print_dependencies (void);
 segT   subseg_get (const char *, int);
 
+extern void app_c_finalize (void);
+extern void depend_c_finalize (void);
+
 const char *remap_debug_filename (const char *);
 void add_debug_prefix_map (const char *);
 
@@ -631,4 +634,13 @@ flag_size_check;
  #error "Octets per byte conflicts with its power-of-two definition!"
 #endif
 
+extern struct ctimer *gas_timer;
+
+#define GAS_TIMER_PUSH(ITEM_NAME) \
+  do { CTIMER_PUSH (gas_timer, (ITEM_NAME)); } while (0)
+
+#define GAS_TIMER_POP() \
+  do { CTIMER_POP (gas_timer); } while (0)
+
+
 #endif /* GAS */
diff --git a/gas/asmain.c b/gas/asmain.c
new file mode 100644
index 0000000..687ae73
--- /dev/null
+++ b/gas/asmain.c
@@ -0,0 +1,35 @@
+/* asmain.c - "main" for standalone "as" program.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS 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.
+
+   GAS 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 GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "libgas.h"
+
+int
+main (int argc, char ** argv)
+{
+  int result = gas_main (argc,
+                         argv,
+                         1, /* standalone */
+                         NULL); /* timer */
+
+  /* Use xexit instead of return, because under VMS environments they
+     may not place the same interpretation on the value given.  */
+  xexit (result);
+}
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 4d7a8a7..cb44a10 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -1947,10 +1947,11 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+static segT obj_elf_ident_comment_section;
+
 static void
 obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
 {
-  static segT comment_section;
   segT old_section = now_seg;
   int old_subsection = now_subseg;
 
@@ -1958,14 +1959,14 @@ obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
   md_flush_pending_output ();
 #endif
 
-  if (!comment_section)
+  if (!obj_elf_ident_comment_section)
     {
       char *p;
-      comment_section = subseg_new (".comment", 0);
-      bfd_set_section_flags (stdoutput, comment_section,
+      obj_elf_ident_comment_section = subseg_new (".comment", 0);
+      bfd_set_section_flags (stdoutput, obj_elf_ident_comment_section,
      SEC_READONLY | SEC_HAS_CONTENTS
      | SEC_MERGE | SEC_STRINGS);
-      comment_section->entsize = 1;
+      obj_elf_ident_comment_section->entsize = 1;
 #ifdef md_elf_section_change_hook
       md_elf_section_change_hook ();
 #endif
@@ -1973,7 +1974,7 @@ obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
       *p = 0;
     }
   else
-    subseg_set (comment_section, 0);
+    subseg_set (obj_elf_ident_comment_section, 0);
   stringer (8 + 1);
   subseg_set (old_section, old_subsection);
 }
@@ -2681,3 +2682,15 @@ const struct format_ops elf_format_ops =
   0,
   elf_adjust_symtab
 };
+
+/* FIXME.  */
+extern void config_obj_elf_c_finalize (void);
+
+/* Reset all global state within this source file.  */
+
+void
+config_obj_elf_c_finalize (void)
+{
+  obj_elf_ident_comment_section = NULL;
+  /* TODO: anything else? */
+}
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index db263ee..cbc6e13 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -10655,3 +10655,82 @@ handle_large_common (int small ATTRIBUTE_UNUSED)
     }
 }
 #endif /* OBJ_ELF || OBJ_MAYBE_ELF */
+
+/* FIXME.  */
+extern void config_tc_i386_c_finalize (void);
+
+/* Reset all global state within this source file.  */
+
+void
+config_tc_i386_c_finalize (void)
+{
+#ifdef TE_LINUX
+  flag_compress_debug = COMPRESS_DEBUG_ZLIB;
+#endif
+  default_arch = DEFAULT_ARCH;
+  CLEAR_VAR (rc_op);
+  CLEAR_VAR (mask_op);
+  CLEAR_VAR (broadcast_op);
+  CLEAR_VAR (i);
+  current_templates = NULL;
+  CLEAR_VAR (disp_expressions);
+  CLEAR_VAR (im_expressions);
+  this_operand = -1;
+  flag_code = 0;
+  object_64bit = 0;
+  disallow_64bit_reloc = 0;
+  use_rela_relocations = 0;
+#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \
+     || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+     || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+  x86_elf_abi = I386_ABI;
+#endif
+#if defined (TE_PE) || defined (TE_PEP)
+  use_big_obj = 0;
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+  no_shared = 0;
+#endif
+  intel_syntax = 0;
+  intel_mnemonic = !SYSV386_COMPAT;
+  old_gcc = OLDGCC_COMPAT;
+  allow_pseudo_reg = 0;
+  allow_naked_reg = 0;
+  add_bnd_prefix = 0;
+  allow_index_reg = 0;
+  omit_lock_prefix = 0;
+  operand_check = check_warning;
+  register_prefix = "%";
+  stackop_size = '\0';
+  optimize_align_code = 1;
+  quiet_warnings = 0;
+  cpu_arch_name = NULL;
+  cpu_sub_arch_name = NULL;
+  //cpu_arch_flags = CPU_UNKNOWN_FLAGS;
+  cpu_arch_tune_set = 0;
+  cpu_arch_tune = PROCESSOR_UNKNOWN;
+  CLEAR_VAR (cpu_arch_tune_flags);
+  cpu_arch_isa = PROCESSOR_UNKNOWN;
+  CLEAR_VAR (cpu_arch_isa_flags);
+  no_cond_jump_promotion = 0;
+  sse2avx = 0;
+  avxscalar = 0;
+  evexlig = 0;
+  evexwig = 0;
+  evexrcig = rne;
+  GOT_symbol = NULL;
+  x86_dwarf2_return_column = 0;
+  x86_cie_data_alignment = 0;
+  input_line_pointer = NULL;
+  if (op_hash)
+    {
+      hash_die (op_hash);
+      op_hash = NULL;
+    }
+  if (reg_hash)
+    {
+      hash_die (reg_hash);
+      reg_hash= NULL;
+    }
+  cons_sign = -1;
+}
diff --git a/gas/depend.c b/gas/depend.c
index 1a1ec63..cc8a3e0 100644
--- a/gas/depend.c
+++ b/gas/depend.c
@@ -205,3 +205,13 @@ print_dependencies (void)
   if (fclose (f))
     as_warn (_("can't close `%s'"), dep_file);
 }
+
+/* Reset all global state within depend.c.  */
+
+void
+depend_c_finalize (void)
+{
+  dep_file = NULL;
+  dep_chain = NULL;
+  column = 0;
+}
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index ef97e18..1527624 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -2084,3 +2084,15 @@ cfi_finish (void)
 {
 }
 #endif /* TARGET_USE_CFIPOP */
+
+/* Reset all global state within dw2gencfi.c.  */
+
+void
+dw2gencfi_c_finalize (void)
+{
+  dwcfi_hash = NULL;
+  all_fde_data = NULL;
+  last_fde_data = &all_fde_data;
+  cie_root = NULL;
+  cfi_sections = CFI_EMIT_eh_frame;
+}
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 6e2c50e..009eee3 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -133,4 +133,6 @@ extern struct fde_entry *all_fde_data;
 #define CFI_val_encoded_addr 0x105
 #define CFI_label 0x106
 
+extern void dw2gencfi_c_finalize (void);
+
 #endif /* DW2GENCFI_H */
diff --git a/gas/libgas.h b/gas/libgas.h
new file mode 100644
index 0000000..479a990
--- /dev/null
+++ b/gas/libgas.h
@@ -0,0 +1,41 @@
+/* API for the GNU Assembler.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program 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 of the License, or
+   (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef LIBGAS_H
+#define LIBGAS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ctimer;
+
+extern int
+gas_main (int argc,
+          char **argv,
+          int standalone,
+          struct ctimer *timer);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* ! defined (LIBGAS_H) */
diff --git a/gas/macro.c b/gas/macro.c
index 119b1bf..76e8fdf 100644
--- a/gas/macro.c
+++ b/gas/macro.c
@@ -1401,3 +1401,21 @@ expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
 
   return err;
 }
+
+/* Reset all global state within macro.c.  */
+
+void
+macro_c_finalize (void)
+{
+  if (macro_hash)
+    {
+      hash_die (macro_hash);
+      macro_hash = NULL;
+    }
+  macro_defined = 0;
+  macro_alternate = 0;
+  macro_mri = 0;
+  macro_strip_at = 0;
+  macro_expr = 0;
+  macro_number = 0;
+}
diff --git a/gas/macro.h b/gas/macro.h
index 46b1575..a0c48c3 100644
--- a/gas/macro.h
+++ b/gas/macro.h
@@ -93,4 +93,6 @@ extern int check_macro (const char *, sb *, const char **, macro_entry **);
 extern void delete_macro (const char *);
 extern const char *expand_irp (int, size_t, sb *, sb *, size_t (*) (sb *));
 
+extern void macro_c_finalize (void);
+
 #endif
diff --git a/gas/output-file.c b/gas/output-file.c
index 94dbc2e..769bd43 100644
--- a/gas/output-file.c
+++ b/gas/output-file.c
@@ -71,3 +71,11 @@ output_file_close (char *filename)
     as_fatal (_("can't close %s: %s"), filename,
       bfd_errmsg (bfd_get_error ()));
 }
+
+/* Reset all global state within output-file.c.  */
+
+void
+output_file_c_finalize (void)
+{
+  stdoutput = NULL;
+}
diff --git a/gas/output-file.h b/gas/output-file.h
index bfba284..3e8d848 100644
--- a/gas/output-file.h
+++ b/gas/output-file.h
@@ -21,5 +21,6 @@
 void output_file_append (char *where, long length, char *filename);
 void output_file_close (char *filename);
 void output_file_create (char *name);
+void output_file_c_finalize (void);
 
 /* end of output-file.h */
diff --git a/gas/read.c b/gas/read.c
index 2224c0e..d49b122 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -6170,3 +6170,50 @@ find_end_of_line (char *s, int mri_string)
 {
   return _find_end_of_line (s, mri_string, 0, 0);
 }
+
+/* Reset all global state within read.c.  */
+
+void
+read_c_finalize (void)
+{
+  input_line_pointer = NULL;
+  /* FIXME: char lex_type[256] = { */
+  /* FIXME:char is_end_of_line[256] = { */
+#ifndef TC_CASE_SENSITIVE
+  CLEAR_VAR (original_case_string);
+#endif
+  buffer = NULL;
+  buffer_limit = NULL;
+  target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+  include_dirs = NULL;
+  include_dir_count = 0;
+  include_dir_maxlen = 1;
+#ifndef WORKING_DOT_WORD
+  broken_words = NULL;
+  new_broken_words = 0;
+#endif
+  abs_section_offset = 0;
+  line_label = NULL;
+  mri_common_symbol = NULL;
+  mri_pending_align = 0;
+#ifndef NO_LISTING
+#ifdef OBJ_ELF
+  dwarf_file_string = 0;
+#endif
+#endif
+#ifdef HANDLE_BUNDLE
+  bundle_align_p2 = 0;
+  bundle_lock_frag = NULL;
+  bundle_lock_frchain = NULL;
+  bundle_lock_depth = 0;
+#endif
+  if (po_hash)
+    {
+      hash_die (po_hash);
+      po_hash = NULL;
+    }
+  pop_override_ok = 0;
+  pop_table_name = NULL;
+  scrub_string = NULL;
+  scrub_string_end = NULL;
+}
diff --git a/gas/read.h b/gas/read.h
index d7ac6ce..7d17a7a 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -198,3 +198,5 @@ extern void s_xstab (int what);
 extern void s_rva (int);
 extern void s_incbin (int);
 extern void s_weakref (int);
+
+extern void read_c_finalize (void);
diff --git a/gas/subsegs.c b/gas/subsegs.c
index 4e422de..331ec29 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -325,4 +325,14 @@ subsegs_print_statistics (FILE *file)
     }
 }
 
+/* Reset all global state within subsegs.c.  */
+
+void
+subsegs_c_finalize (void)
+{
+  frchain_now = NULL;
+  obstack_free (&frchains, 0);
+  CLEAR_VAR (dummy_frag);
+}
+
 /* end of subsegs.c */
diff --git a/gas/subsegs.h b/gas/subsegs.h
index 82837a3..78e5b2f 100644
--- a/gas/subsegs.h
+++ b/gas/subsegs.h
@@ -117,3 +117,4 @@ typedef struct segment_info_struct {
 extern symbolS *section_symbol (segT);
 
 extern void subsegs_print_statistics (FILE *);
+extern void subsegs_c_finalize (void);
diff --git a/gas/symbols.c b/gas/symbols.c
index 07362bb..d660d53 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -3248,3 +3248,44 @@ symbol_relc_make_expr (expressionS * exp)
 }
 
 #endif
+
+/* Reset all global state within symbols.c.  */
+
+void
+symbols_c_finalize (void)
+{
+  symbols_case_sensitive = 1;
+  if (sy_hash)
+    {
+      hash_die (sy_hash);
+      sy_hash = NULL;
+    }
+  if (local_hash)
+    {
+      hash_die (local_hash);
+      local_hash = NULL;
+    }
+  symbol_rootP = NULL;
+  symbol_lastP = NULL;
+  CLEAR_VAR (abs_symbol);
+  CLEAR_VAR (dot_symbol);
+  obstack_free (&notes, 0);
+  CLEAR_VAR (notes);
+#ifdef TE_PE
+  an_external_name = NULL;
+#endif
+  local_symbol_count = 0;
+  local_symbol_conversion_count = 0;
+  dollar_labels = NULL;
+  dollar_label_instances = NULL;
+  dollar_label_defines = NULL;
+  dollar_label_count = 0;
+  dollar_label_max = 0;
+  CLEAR_VAR (fb_low_counter);
+  fb_labels = NULL;
+  fb_label_instances = NULL;
+  fb_label_count = 0;
+  fb_label_max = 0;
+  indent_level = 0;
+  max_indent_level = 8;
+}
diff --git a/gas/symbols.h b/gas/symbols.h
index cf4f30f..8a88383 100644
--- a/gas/symbols.h
+++ b/gas/symbols.h
@@ -213,3 +213,5 @@ void symbol_set_obj (symbolS *, OBJ_SYMFIELD_TYPE *);
 TC_SYMFIELD_TYPE *symbol_get_tc (symbolS *);
 void symbol_set_tc (symbolS *, TC_SYMFIELD_TYPE *);
 #endif
+
+extern void symbols_c_finalize (void);
diff --git a/gas/write.c b/gas/write.c
index bc76962..9562a7b 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -2907,3 +2907,18 @@ print_fixup (fixS *fixp)
   TC_FIX_DATA_PRINT (stderr, fixp);
 #endif
 }
+
+/* Reset all global state within write.c.  */
+
+void
+write_c_finalize (void)
+{
+  finalize_syms = 0;
+  symbol_table_frozen = 0;
+  abs_section_sym = NULL;
+  dot_value = 0;
+  dot_frag = NULL;
+  reloc_list = NULL;
+  frags_chained = 0;
+  n_fixups = 0;
+}
diff --git a/gas/write.h b/gas/write.h
index e0b5b36..63026f9 100644
--- a/gas/write.h
+++ b/gas/write.h
@@ -184,5 +184,6 @@ extern fixS *fix_new_exp
   (fragS * frag, int where, int size, expressionS *exp, int pcrel,
    bfd_reloc_code_real_type r_type);
 extern void write_print_statistics (FILE *);
+extern void write_c_finalize (void);
 
 #endif /* __write_h__ */
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 08/16] libiberty.h: Provide a CLEAR_VAR macro

David Malcolm
In reply to this post by David Malcolm
include/ChangeLog:
        * libiberty.h (CLEAR_VAR): New macro.
---
 include/libiberty.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/libiberty.h b/include/libiberty.h
index b33dd65..93e4131 100644
--- a/include/libiberty.h
+++ b/include/libiberty.h
@@ -699,6 +699,10 @@ extern void stack_limit_increase (unsigned long);
 
 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
 
+/* Fill an lvalue with zero bits.  */
+#define CLEAR_VAR(S) \
+  do { memset (&(S), 0, sizeof (S)); } while (0)
+
 /* Drastically simplified alloca configurator.  If we're using GCC,
    we use __builtin_alloca; otherwise we use the C alloca.  The C
    alloca is always available.  You can override GCC by defining
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 07/16] binutils: bfd: Implement bfd_uninit

David Malcolm
In reply to this post by David Malcolm
This patch adds a new API entrypoint to libbfd: bfd_uninit,
which resets global state within the library.

The patch (and subsequent ones) follow a pattern I used in gcc for
cleaning up global state: for each source file e.g. foo.c to have a
function:
  extern void foo_c_finalize (void);
to explicitly reset all statically-allocated global data back to its
initial value.

Two warts: where should prototypes for internal functions be declared?
I need to put an:
  extern void section_c_finalize (void);
into an internal header, but I don't want to expose that as API.
Similarly, is there a way to use a linker script to hide the function?

bfd/ChangeLog:
        * bfd-in2.h: Regenerate.
        * elflink.c (elf_flags_to_names): Make const.
        * init.c (bfd_uninit): New function.
        * section.c (reinit_BFD_FAKE_SECTION): New function.
        (reinit_STD_SECTION): New function.
        (section_c_finalize): New function.
---
 bfd/bfd-in2.h |  2 ++
 bfd/elflink.c |  2 +-
 bfd/init.c    | 20 ++++++++++++++++++++
 bfd/section.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index ade49ff..cba57b3 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1016,6 +1016,8 @@ extern bfd_boolean v850_elf_set_note
 /* Extracted from init.c.  */
 void bfd_init (void);
 
+void bfd_uninit (void);
+
 /* Extracted from opncls.c.  */
 /* Set to N to open the next N BFDs using an alternate id space.  */
 extern unsigned int bfd_use_reserved_id;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 6efe1e4..d8c1171 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -12639,7 +12639,7 @@ typedef struct
   flagword flag_value;
 } elf_flags_to_name_table;
 
-static elf_flags_to_name_table elf_flags_to_names [] =
+static const elf_flags_to_name_table elf_flags_to_names [] =
 {
   { "SHF_WRITE", SHF_WRITE },
   { "SHF_ALLOC", SHF_ALLOC },
diff --git a/bfd/init.c b/bfd/init.c
index 323f0ac..e4fae11 100644
--- a/bfd/init.c
+++ b/bfd/init.c
@@ -52,3 +52,23 @@ void
 bfd_init (void)
 {
 }
+
+/*
+FUNCTION
+ bfd_uninit
+
+SYNOPSIS
+ void bfd_uninit (void);
+
+DESCRIPTION
+ Purge internal state within the library.
+*/
+
+/* FIXME: where should prototypes for internal functions be declared?  */
+extern void section_c_finalize (void);
+
+void
+bfd_uninit (void)
+{
+  section_c_finalize ();
+}
diff --git a/bfd/section.c b/bfd/section.c
index 24422bf..f1d445f 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -1666,3 +1666,56 @@ bfd_generic_discard_group (bfd *abfd ATTRIBUTE_UNUSED,
 {
   return TRUE;
 }
+
+/* Helper function for reinit_STD_SECTION
+   Reset the given section back to its initial state.
+   Based on macro BFD_FAKE_SECTION. */
+
+static void
+reinit_BFD_FAKE_SECTION (asection *sec,
+                         int flags,
+                         const struct bfd_symbol *sym,
+                         const char *name,
+                         int idx)
+{
+  /* Zero it.  */
+  memset (sec, 0, sizeof (*sec));
+
+  /* Now fixup the things that BFD_FAKE_SECTION makes nonzero.  */
+  sec->name = name;
+  sec->id = idx;
+  sec->flags = flags;
+  sec->gc_mark = 1;
+  sec->output_section = sec;
+  sec->symbol = (struct bfd_symbol *) sym; /* (cast away constness) */
+  sec->symbol_ptr_ptr = &sec->symbol;
+}
+
+/* Helper function for section_c_finalize.
+   Reset one of the global sections within _bfd_std_section back to its
+   initial state.
+   Based on macros STD_SECTION and BFD_FAKE_SECTION. */
+
+static void
+reinit_STD_SECTION (const char *name,
+                    int idx,
+                    int flags)
+{
+  asection *sec = &_bfd_std_section[idx];
+  reinit_BFD_FAKE_SECTION (sec, flags, &global_syms[idx], name, idx);
+}
+
+/* FIXME: where should prototypes for internal functions be declared?  */
+extern void
+section_c_finalize (void);
+
+/* Reset global state within section.c back to its original state.  */
+
+void
+section_c_finalize (void)
+{
+  reinit_STD_SECTION (BFD_COM_SECTION_NAME, 0, SEC_IS_COMMON);
+  reinit_STD_SECTION (BFD_UND_SECTION_NAME, 1, 0);
+  reinit_STD_SECTION (BFD_ABS_SECTION_NAME, 2, 0);
+  reinit_STD_SECTION (BFD_IND_SECTION_NAME, 3, 0);
+}
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 09/16] libiberty.h: Provide CTIMER_PUSH/POP

David Malcolm
In reply to this post by David Malcolm
gcc provides a timing mechanism, with a C++-based API.

This patch provide a C-based API so that we can call into it
from binutils.

include/ChangeLog:
        * libiberty.h (struct ctimer): New.
        (CTIMER_PUSH): New macro.
        (CTIMER_POP): New macro.
---
 include/libiberty.h | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/include/libiberty.h b/include/libiberty.h
index 93e4131..6e3a391 100644
--- a/include/libiberty.h
+++ b/include/libiberty.h
@@ -735,6 +735,38 @@ extern unsigned long libiberty_len;
    (char *) memcpy (libiberty_nptr, libiberty_optr, libiberty_len))
 #endif
 
+/* Support for hooking into gcc's timing mechanism
+   (class timer), from a pure C API, withough needing to link
+   against any symbols. */
+
+struct ctimer
+{
+   void (*push) (struct ctimer *t, const char *item_name);
+   void (*pop) (struct ctimer *t);
+};
+
+/* Macros for pushing/popping named timing items, so we can write e.g.:
+
+     CTIMER_PUSH (some_timer, "init");
+     init ();
+     CTIMER_POP ();
+
+   and have it redirected into GCC's timing mechanism.
+
+   Typically, CTIMER is NULL, and this does nothing.  */
+
+#define CTIMER_PUSH(CTIMER, ITEM_NAME)          \
+  do {                                          \
+    if (CTIMER)                                 \
+      (CTIMER)->push ((CTIMER), (ITEM_NAME));   \
+  } while (0)
+
+#define CTIMER_POP(CTIMER)                      \
+  do {                                          \
+    if (CTIMER)                                 \
+      (CTIMER)->pop (CTIMER);                   \
+  } while (0)
+
 #ifdef __cplusplus
 }
 #endif
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 06/16] gcc: driver: add timevars for as, collect2, ld

David Malcolm
In reply to this post by David Malcolm
gcc/ChangeLog:
        * gcc.c (execute): Identify certain commands, and account their
        time to specific timevars.
        * gcc.h (driver::get_timer): New accessor.
        * timevar.def (TV_DRIVER_EXECUTE_AS): New.
        (TV_DRIVER_EXECUTE_COLLECT2): New.
        (TV_DRIVER_EXECUTE_LD): New.
        (TV_DRIVER_EXECUTE_OTHER): New.
---
 gcc/gcc.c       | 19 +++++++++++++++++++
 gcc/gcc.h       |  2 ++
 gcc/timevar.def |  4 ++++
 3 files changed, 25 insertions(+)

diff --git a/gcc/gcc.c b/gcc/gcc.c
index 46e750d..93f41ec 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -2845,6 +2845,25 @@ execute (void)
     if (strcmp (arg, "|") == 0)
       n_commands++;
 
+  /* 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;
+    }
+  auto_timevar tv (driver_timer, tv_id);
+
   /* Get storage for each command.  */
   commands = (struct command *) alloca (n_commands * sizeof (struct command));
 
diff --git a/gcc/gcc.h b/gcc/gcc.h
index cfff8ef..15bc11d 100644
--- a/gcc/gcc.h
+++ b/gcc/gcc.h
@@ -37,6 +37,8 @@ class driver
   int main (int argc, char **argv);
   void finalize ();
 
+  timer *get_timer () const { return m_timer; }
+
  private:
   void set_progname (const char *argv0) const;
   void expand_at_files (int *argc, char ***argv) const;
diff --git a/gcc/timevar.def b/gcc/timevar.def
index d04a729..ce9236d 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -291,6 +291,10 @@ DEFTIMEVAR (TV_ASSEMBLE     , "assemble JIT code")
 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_EXECUTE_COLLECT2, "driver: executing \"collect2\"")
+DEFTIMEVAR (TV_DRIVER_EXECUTE_LD     , "driver: executing \"ld\"")
+DEFTIMEVAR (TV_DRIVER_EXECUTE_OTHER  , "driver: executing subprocess")
 DEFTIMEVAR (TV_LINK     , "link JIT code")
 DEFTIMEVAR (TV_LOAD     , "load JIT result")
 DEFTIMEVAR (TV_JIT_ACQUIRING_MUTEX   , "acquiring JIT mutex")
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 04/16] gcc: Don't keep reinitializing RTL

David Malcolm
In reply to this post by David Malcolm
This may be something of a hack; it appears to work for x86_64.

Before:
initialize rtl          :   1.50 (63%) usr   0.02 (10%) sys   1.34 (23%) wall    1156 kB ( 5%) ggc
rest of compilation     :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 1%) wall     232 kB ( 1%) ggc
TOTAL                 :   2.37             0.21             5.86              21438 kB
./test-benchmark.c.exe: 100 iterations at optlevel 0 took a total of 5.870s (0.059s per iteration)

After:
initialize rtl          :   0.01 ( 1%) usr   0.01 ( 4%) sys   0.02 ( 0%) wall      12 kB ( 0%) ggc
rest of compilation     :   0.03 ( 3%) usr   0.01 ( 4%) sys   0.03 ( 1%) wall     232 kB ( 1%) ggc
TOTAL                 :   1.03             0.24             4.53              20294 kB
./test-benchmark.c.exe: 100 iterations at optlevel 0 took a total of 4.530s (0.045s per iteration)

(i.e. saves about 10-15ms per iteration)

gcc/ChangeLog:
        * ira-costs.c (ira_costs_c_finalize): Delete.
        * ira.h (void ira_costs_c_finalize): Delete.
        * toplev.c (lang_dependent_init_target): Remove assertion tha
        this_target_rtl->target_specific_initialized is false.
        (finalize): Remove call to lra_finish_once.
        (toplev::finalize): Don't unset "rtl_initialized" and
        "this_target_rtl->target_specific_initialized".  Remove call t
        ira_costs_c_finalize.
---
 gcc/ira-costs.c | 6 ------
 gcc/ira.h       | 3 ---
 gcc/toplev.c    | 8 --------
 3 files changed, 17 deletions(-)

diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c
index fa6aec4..cfe416d 100644
--- a/gcc/ira-costs.c
+++ b/gcc/ira-costs.c
@@ -2367,9 +2367,3 @@ ira_adjust_equiv_reg_cost (unsigned regno, int cost)
   else
     regno_equiv_gains[regno] += cost;
 }
-
-void
-ira_costs_c_finalize (void)
-{
-  this_target_ira_int->free_ira_costs ();
-}
diff --git a/gcc/ira.h b/gcc/ira.h
index 5b52cb1..306692d 100644
--- a/gcc/ira.h
+++ b/gcc/ira.h
@@ -206,7 +206,4 @@ extern bool ira_bad_reload_regno (int, rtx, rtx);
 
 extern void ira_adjust_equiv_reg_cost (unsigned, int);
 
-/* ira-costs.c */
-extern void ira_costs_c_finalize (void);
-
 #endif /* GCC_IRA_H */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 3527b5d..12806d0 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1806,8 +1806,6 @@ lang_dependent_init_target (void)
      front end is initialized.  It also depends on the HAVE_xxx macros
      generated from the target machine description.  */
   init_optabs ();
-
-  gcc_assert (!this_target_rtl->target_specific_initialized);
 }
 
 /* Perform initializations that are lang-dependent or target-dependent.
@@ -1998,8 +1996,6 @@ finalize (bool no_backend)
       statistics_fini ();
 
       g->get_passes ()->finish_optimization_passes ();
-
-      lra_finish_once ();
     }
 
   if (mem_report)
@@ -2207,9 +2203,6 @@ toplev::main (int argc, char **argv)
 void
 toplev::finalize (void)
 {
-  rtl_initialized = false;
-  this_target_rtl->target_specific_initialized = false;
-
   /* Needs to be called before cgraph_c_finalize since it uses symtab.  */
   ipa_reference_c_finalize ();
 
@@ -2218,7 +2211,6 @@ toplev::finalize (void)
   dwarf2out_c_finalize ();
   gcse_c_finalize ();
   ipa_cp_c_finalize ();
-  ira_costs_c_finalize ();
   params_c_finalize ();
 
   finalize_options_struct (&global_options);
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 11/16] binutils: gas/Makefile.am: Add libgas.la

David Malcolm
In reply to this post by David Malcolm
This patch introduces a libgas.la, using it for the "as" executable.

I'm not convinced I've got this correct, but it seems to work well
enough to prototype the embedding within libgccjit.so.
---
 gas/Makefile.am | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 5161b5e..c87546a 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -402,14 +402,22 @@ GASLIBS = @OPCODES_LIB@ ../bfd/libbfd.la ../libiberty/libiberty.a
 # Files to be copied away after each stage in building.
 STAGESTUFF = *.@OBJEXT@ $(noinst_PROGRAMS)
 
-as_new_SOURCES = $(GAS_CFILES)
-as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
- $(extra_objects) $(GASLIBS) $(LIBINTL) $(LIBM)
-as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
- $(extra_objects) $(GASLIBS) $(LIBINTL_DEP)
-EXTRA_as_new_SOURCES = $(CFILES) $(HFILES) $(TARGET_CPU_CFILES) \
- $(TARGET_CPU_HFILES) $(OBJ_FORMAT_CFILES) $(OBJ_FORMAT_HFILES) \
- $(TARG_ENV_CFILES) $(CONFIG_ATOF_CFILES) $(MULTI_CFILES)
+include_HEADERS = libgas.h
+
+as_new_SOURCES = asmain.c
+as_new_LDADD = libgas.la
+
+lib_LTLIBRARIES = libgas.la
+
+libgas_la_SOURCES = $(GAS_CFILES) \
+ $(TARG_CPU_C) \
+ $(OBJ_FORMAT_C) \
+ $(ATOF_TARG_C)
+libgas_la_LIBADD = $(extra_objects) $(GASLIBS) $(LIBINTL) $(LIBM)
+libgas_la_DEPENDENCIES = $(extra_objects) $(GASLIBS) $(LIBINTL_DEP)
+EXTRA_libgas_la_SOURCES = $(CFILES)
+libgas_la_CFLAGS = $(AM_CFLAGS)
+# -fPIC ???
 
 EXPECT = expect
 RUNTEST = runtest
@@ -437,7 +445,7 @@ check-DEJAGNU: site.exp
 
 # The m68k operand parser.
 
-EXTRA_as_new_SOURCES += config/m68k-parse.y config/bfin-parse.y
+EXTRA_libgas_la_SOURCES += config/m68k-parse.y config/bfin-parse.y
 
 # If m68k-parse.y is in a different directory, then ylwrap will use an
 # absolute path when it invokes yacc, which will cause yacc to put the
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 02/16] gcc: Embed the driver in-process within libgccjit

David Malcolm
In reply to this post by David Malcolm
Provide a way to clean up state within the driver code, and use this
from libgccjit to embed it in-process, rather that via pex.  Part of
this requires restoring the environment after any putenv calls, so the
patch introduces an env_manager class.  No effort is made to restore
the environment for the classic use-case.

This embedding gives a slight performance win for
jit.dg/test-benchmark.c, and enables bigger performance gains in
followup patches.

I've fixed the worst of the memory leaks, but this does still
leak somewhat.

gcc/ChangeLog:
        * gcc-main.c (main): Add params to driver ctor.
        * gcc.c (class env_manager): New.
        (env): New global.
        (env_manager::init): New.
        (env_manager::getenv): New.
        (env_manager::xputenv): New.
        (env_manager::restore): New.
        Poison genenv and putenv.
        (DEFAULT_TARGET_SYSTEM_ROOT): New.
        (target_system_root): Update initialization to use
        DEFAULT_TARGET_SYSTEM_ROOT.
        (struct spec_list): Add field "default_ptr".
        (INIT_STATIC_SPEC): Initialize new field "default_ptr".
        (init_spec): Likewise.
        (set_spec): Clear field "default_ptr".
        (read_specs): Free "spec" and "buffer".
        (xputenv): Reimplement in terms of env_manager.
        (process_command): Replace ::getenv calls with calls to the
        env_manager singleton.
        (process_brace_body): Free string in three places.
        (driver::driver): New.
        (driver::~driver): New.
        (used_arg): Convert from a function to...
        (class used_arg_t): ...this class, and...
        (used_arg): ...this new global instance.
        (used_arg_t::finalize): New function.
        (getenv_spec_function): Add "const" to local "value".  Replace
        ::getenv call with call to the env_manager singleton.
        (path_prefix_reset): New function.
        (driver::finalize): New function.
        * gcc.h (driver::driver): New.
        (driver::~driver): New.
        (driver::finalize): New.

gcc/jit/ChangeLog:
        * jit-playback.c (gcc_driver_name): New global.
        (gcc:jit::playback::context::invoke_driver): Split out second
        half into...
        (gcc::jit::playback::context::invoke_embedded_driver): ...this new
        function, and...
        (gcc::jit::playback::context::invoke_external_driver): ...this new
        function.
        * jit-playback.h
        (gcc::jit::playback::context::invoke_embedded_driver): New.
        (gcc::jit::playback::context::invoke_external_driver): New.
        * notes.txt: Show invocation of embedded copy of driver.
---
 gcc/gcc-main.c         |   3 +-
 gcc/gcc.c              | 392 ++++++++++++++++++++++++++++++++++++++++++++++---
 gcc/gcc.h              |   3 +
 gcc/jit/jit-playback.c |  49 ++++++-
 gcc/jit/jit-playback.h |   8 +
 gcc/jit/notes.txt      |   8 +-
 6 files changed, 432 insertions(+), 31 deletions(-)

diff --git a/gcc/gcc-main.c b/gcc/gcc-main.c
index 230ba48..a0aaa3c 100644
--- a/gcc/gcc-main.c
+++ b/gcc/gcc-main.c
@@ -40,7 +40,8 @@ extern int main (int, char **);
 int
 main (int argc, char **argv)
 {
-  driver d;
+  driver d (false, /* can_finalize */
+    false); /* debug */
 
   return d.main (argc, argv);
 }
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 8f01e42..683392d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -44,6 +44,127 @@ compilation is specified by a string called a "spec".  */
 #include "vec.h"
 #include "filenames.h"
 
+
+
+/* Manage the manipulation of env vars.  We poison getenv and putenv, so that
+   all enviroment-handling is done through this class.  */
+
+class env_manager
+{
+ public:
+  void init (bool can_restore, bool debug);
+  const char *getenv (const char *name);
+  void xputenv (const char *string);
+  void restore ();
+
+ private:
+  bool m_can_restore;
+  bool m_debug;
+  struct kv
+  {
+    char *m_key;
+    char *m_value;
+  };
+  vec<kv> m_keys;
+
+};
+
+/* The singleton instance of class env_manager.  */
+
+static env_manager env;
+
+/* Initializer for class env_manager.
+
+   We can't do this as a constructor since we have a statically
+   allocated instance ("env" above).  */
+
+void
+env_manager::init (bool can_restore, bool debug)
+{
+  m_can_restore = can_restore;
+  m_debug = debug;
+}
+
+/* Get the value of NAME within the environment.  Essentially
+   a wrapper for ::getenv, but adding logging, and the possibility
+   of caching results.  */
+
+const char *
+env_manager::getenv (const char *name)
+{
+  const char *result = ::getenv (name);
+  if (m_debug)
+    fprintf (stderr, "env_manager::getenv (%s) -> %s\n", name, result);
+  return result;
+}
+
+/* Put the given KEY=VALUE entry STRING into the environment.
+   If the env_manager was initialized with CAN_RESTORE set, then
+   also record the old value of KEY within the environment, so that it
+   can be later restored.  */
+
+void
+env_manager::xputenv (const char *string)
+{
+  if (m_debug)
+    fprintf (stderr, "env_manager::xputenv (%s)\n", string);
+  if (verbose_flag)
+    fnotice (stderr, "%s\n", string);
+
+  if (m_can_restore)
+    {
+      char *equals = strchr (const_cast <char *> (string), '=');
+      gcc_assert (equals);
+
+      struct kv kv;
+      kv.m_key = strndup (string, equals - string);
+      const char *cur_value = ::getenv (kv.m_key);
+      if (m_debug)
+ fprintf (stderr, "saving old value: %s\n",cur_value);
+      kv.m_value = cur_value ? xstrdup (cur_value) : NULL;
+      m_keys.safe_push (kv);
+    }
+
+  ::putenv (CONST_CAST (char *, string));
+}
+
+/* Undo any xputenv changes made since last restore.
+   Can only be called if the env_manager was initialized with
+   CAN_RESTORE enabled.  */
+
+void
+env_manager::restore ()
+{
+  unsigned int i;
+  struct kv *item;
+
+  gcc_assert (m_can_restore);
+
+  FOR_EACH_VEC_ELT_REVERSE (m_keys, i, item)
+    {
+      if (m_debug)
+ printf ("restoring saved key: %s value: %s\n", item->m_key, item->m_value);
+      if (item->m_value)
+ {
+  char *newstr = concat (item->m_key, "=", item->m_value, NULL);
+  ::putenv (newstr);
+ }
+      else
+ ::unsetenv (item->m_key);
+      free (item->m_key);
+      free (item->m_value);
+    }
+
+  m_keys.truncate (0);
+}
+
+/* Forbid other uses of getenv and putenv.  */
+#if (GCC_VERSION >= 3000)
+#pragma GCC poison genenv putenv
+#endif
+
+
+
 /* By default there is no special suffix for target executables.  */
 /* FIXME: when autoconf is fixed, remove the host check - dj */
 #if defined(TARGET_EXECUTABLE_SUFFIX) && defined(HOST_EXECUTABLE_SUFFIX)
@@ -116,10 +237,11 @@ FILE *report_times_to_file = NULL;
    and library files can be found in an alternate location.  */
 
 #ifdef TARGET_SYSTEM_ROOT
-static const char *target_system_root = TARGET_SYSTEM_ROOT;
+#define DEFAULT_TARGET_SYSTEM_ROOT (TARGET_SYSTEM_ROOT)
 #else
-static const char *target_system_root = 0;
+#define DEFAULT_TARGET_SYSTEM_ROOT (0)
 #endif
+static const char *target_system_root = DEFAULT_TARGET_SYSTEM_ROOT;
 
 /* Nonzero means pass the updated target_system_root to the compiler.  */
 
@@ -236,7 +358,6 @@ static const char *validate_switches (const char *, bool);
 static void validate_all_switches (void);
 static inline void validate_switches_from_spec (const char *, bool);
 static void give_switch (int, int);
-static int used_arg (const char *, int);
 static int default_arg (const char *, int);
 static void set_multilib_dir (void);
 static void print_multilib_info (void);
@@ -1346,10 +1467,12 @@ struct spec_list
   int name_len; /* length of the name */
   bool user_p; /* whether string come from file spec.  */
   bool alloc_p; /* whether string was allocated */
+  const char *default_ptr; /* The default value of *ptr_spec.  */
 };
 
 #define INIT_STATIC_SPEC(NAME,PTR) \
-  { NAME, NULL, PTR, (struct spec_list *) 0, sizeof (NAME) - 1, false, false }
+  { NAME, NULL, PTR, (struct spec_list *) 0, sizeof (NAME) - 1, false, false, \
+    *PTR }
 
 /* List of statically defined specs.  */
 static struct spec_list static_specs[] =
@@ -1516,6 +1639,8 @@ init_spec (void)
       sl->next = next;
       sl->name_len = strlen (sl->name);
       sl->ptr_spec = &sl->ptr;
+      gcc_assert (sl->ptr_spec != NULL);
+      sl->default_ptr = sl->ptr;
       next = sl;
     }
 #endif
@@ -1690,6 +1815,7 @@ set_spec (const char *name, const char *spec, bool user_p)
       sl->alloc_p = 0;
       *(sl->ptr_spec) = "";
       sl->next = specs;
+      sl->default_ptr = NULL;
       specs = sl;
     }
 
@@ -2082,7 +2208,10 @@ read_specs (const char *filename, bool main_p, bool user_p)
   if (! strcmp (suffix, "*link_command"))
     link_command_spec = spec;
   else
-    set_spec (suffix + 1, spec, user_p);
+    {
+      set_spec (suffix + 1, spec, user_p);
+      free (spec);
+    }
  }
       else
  {
@@ -2102,6 +2231,8 @@ read_specs (const char *filename, bool main_p, bool user_p)
 
   if (link_command_spec == 0)
     fatal_error (input_location, "spec file has no spec for linking");
+
+  XDELETEVEC (buffer);
 }
 
 /* Record the names of temporary files we tell compilers to write,
@@ -2444,9 +2575,7 @@ add_to_obstack (char *path, void *data)
 static void
 xputenv (const char *string)
 {
-  if (verbose_flag)
-    fnotice (stderr, "%s\n", string);
-  putenv (CONST_CAST (char *, string));
+  env.xputenv (string);
 }
 
 /* Build a list of search directories from PATHS.
@@ -3910,7 +4039,7 @@ process_command (unsigned int decoded_options_count,
   struct cl_option_handlers handlers;
   unsigned int j;
 
-  gcc_exec_prefix = getenv ("GCC_EXEC_PREFIX");
+  gcc_exec_prefix = env.getenv ("GCC_EXEC_PREFIX");
 
   n_switches = 0;
   n_infiles = 0;
@@ -4015,7 +4144,7 @@ process_command (unsigned int decoded_options_count,
   /* COMPILER_PATH and LIBRARY_PATH have values
      that are lists of directory names with colons.  */
 
-  temp = getenv ("COMPILER_PATH");
+  temp = env.getenv ("COMPILER_PATH");
   if (temp)
     {
       const char *startp, *endp;
@@ -4049,7 +4178,7 @@ process_command (unsigned int decoded_options_count,
  }
     }
 
-  temp = getenv (LIBRARY_PATH_ENV);
+  temp = env.getenv (LIBRARY_PATH_ENV);
   if (temp && *cross_compile == '0')
     {
       const char *startp, *endp;
@@ -4082,7 +4211,7 @@ process_command (unsigned int decoded_options_count,
     }
 
   /* Use LPATH like LIBRARY_PATH (for the CMU build program).  */
-  temp = getenv ("LPATH");
+  temp = env.getenv ("LPATH");
   if (temp && *cross_compile == '0')
     {
       const char *startp, *endp;
@@ -4225,7 +4354,7 @@ process_command (unsigned int decoded_options_count,
 
   if (!compare_debug)
     {
-      const char *gcd = getenv ("GCC_COMPARE_DEBUG");
+      const char *gcd = env.getenv ("GCC_COMPARE_DEBUG");
 
       if (gcd && gcd[0] == '-')
  {
@@ -6157,7 +6286,10 @@ process_brace_body (const char *p, const char *atom, const char *end_atom,
       if (!have_subst)
  {
   if (do_spec_1 (string, 0, NULL) < 0)
-    return 0;
+    {
+      free (string);
+      return 0;
+    }
  }
       else
  {
@@ -6173,12 +6305,16 @@ process_brace_body (const char *p, const char *atom, const char *end_atom,
       {
  if (do_spec_1 (string, 0,
        &switches[i].part1[hard_match_len]) < 0)
-  return 0;
+  {
+    free (string);
+    return 0;
+  }
  /* Pass any arguments this switch has.  */
  give_switch (i, 1);
  suffix_subst = NULL;
       }
  }
+      free (string);
     }
 
   return p;
@@ -6887,6 +7023,19 @@ compare_files (char *cmpfile[])
   return ret;
 }
 
+driver::driver (bool can_finalize, bool debug) :
+  explicit_link_files (NULL),
+  decoded_options (NULL)
+{
+  env.init (can_finalize, debug);
+}
+
+driver::~driver ()
+{
+  XDELETEVEC (explicit_link_files);
+  XDELETEVEC (decoded_options);
+}
+
 /* driver::main is implemented as a series of driver:: method calls.  */
 
 int
@@ -8100,9 +8249,13 @@ static int n_mdswitches;
 /* Check whether a particular argument was used.  The first time we
    canonicalize the switches to keep only the ones we care about.  */
 
-static int
-used_arg (const char *p, int len)
+class used_arg_t
 {
+ public:
+  int operator () (const char *p, int len);
+  void finalize ();
+
+ private:
   struct mswitchstr
   {
     const char *str;
@@ -8111,8 +8264,16 @@ used_arg (const char *p, int len)
     int rep_len;
   };
 
-  static struct mswitchstr *mswitches;
-  static int n_mswitches;
+  mswitchstr *mswitches;
+  int n_mswitches;
+
+};
+
+used_arg_t used_arg;
+
+int
+used_arg_t::operator () (const char *p, int len)
+{
   int i, j;
 
   if (!mswitches)
@@ -8241,6 +8402,14 @@ used_arg (const char *p, int len)
   return 0;
 }
 
+void used_arg_t::finalize ()
+{
+  XDELETEVEC (mswitches);
+  mswitches = NULL;
+  n_mswitches = 0;
+}
+
+
 static int
 default_arg (const char *p, int len)
 {
@@ -8795,7 +8964,7 @@ print_multilib_info (void)
 static const char *
 getenv_spec_function (int argc, const char **argv)
 {
-  char *value;
+  const char *value;
   char *result;
   char *ptr;
   size_t len;
@@ -8803,7 +8972,7 @@ getenv_spec_function (int argc, const char **argv)
   if (argc != 2)
     return NULL;
 
-  value = getenv (argv[0]);
+  value = env.getenv (argv[0]);
   if (!value)
     fatal_error (input_location,
  "environment variable %qs not defined", argv[0]);
@@ -9425,6 +9594,187 @@ convert_white_space (char *orig)
     return orig;
 }
 
+static void
+path_prefix_reset (path_prefix *prefix)
+{
+  struct prefix_list *iter, *next;
+  iter = prefix->plist;
+  while (iter)
+    {
+      next = iter->next;
+      free (const_cast <char *> (iter->prefix));
+      XDELETE (iter);
+      iter = next;
+    }
+  prefix->plist = 0;
+  prefix->max_len = 0;
+}
+
+/* Restore all state within gcc.c to the initial state, so that the driver
+   code can be safely re-run in-process.
+
+   Many const char * variables are referenced by static specs (see
+   INIT_STATIC_SPEC above).  These variables are restored to their default
+   values by a simple loop over the static specs.
+
+   For other variables, we directly restore them all to their initial
+   values (often implicitly 0).
+
+   Free the various obstacks in this file, along with "opts_obstack"
+   from opts.c.
+
+   This function also restores any environment variables that were changed.  */
+
+void
+driver::finalize ()
+{
+  env.restore ();
+  params_c_finalize ();
+  diagnostic_finish (global_dc);
+
+  is_cpp_driver = 0;
+  at_file_supplied = 0;
+  print_help_list = 0;
+  print_version = 0;
+  verbose_only_flag = 0;
+  print_subprocess_help = 0;
+  use_ld = NULL;
+  report_times_to_file = NULL;
+  target_system_root = DEFAULT_TARGET_SYSTEM_ROOT;
+  target_system_root_changed = 0;
+  target_sysroot_suffix = 0;
+  target_sysroot_hdrs_suffix = 0;
+  save_temps_flag = SAVE_TEMPS_NONE;
+  save_temps_prefix = 0;
+  save_temps_length = 0;
+  spec_machine = DEFAULT_TARGET_MACHINE;
+  greatest_status = 1;
+
+  obstack_free (&obstack, NULL);
+  obstack_free (&opts_obstack, NULL); /* in opts.c */
+  obstack_free (&collect_obstack, NULL);
+
+  link_command_spec = LINK_COMMAND_SPEC;
+
+  obstack_free (&multilib_obstack, NULL);
+
+  user_specs_head = NULL;
+  user_specs_tail = NULL;
+
+  /* Within the "compilers" vec, the fields "suffix" and "spec" were
+     statically allocated for the default compilers, but dynamically
+     allocated for additional compilers.  Delete them for the latter. */
+  for (int i = n_default_compilers; i < n_compilers; i++)
+    {
+      free (const_cast <char *> (compilers[i].suffix));
+      free (const_cast <char *> (compilers[i].spec));
+    }
+  XDELETEVEC (compilers);
+  compilers = NULL;
+  n_compilers = 0;
+
+  linker_options.truncate (0);
+  assembler_options.truncate (0);
+  preprocessor_options.truncate (0);
+
+  path_prefix_reset (&exec_prefixes);
+  path_prefix_reset (&startfile_prefixes);
+  path_prefix_reset (&include_prefixes);
+
+  machine_suffix = 0;
+  just_machine_suffix = 0;
+  gcc_exec_prefix = 0;
+  gcc_libexec_prefix = 0;
+  md_exec_prefix = MD_EXEC_PREFIX;
+  md_startfile_prefix = MD_STARTFILE_PREFIX;
+  md_startfile_prefix_1 = MD_STARTFILE_PREFIX_1;
+  multilib_dir = 0;
+  multilib_os_dir = 0;
+  multiarch_dir = 0;
+
+  XDELETEVEC (specs);
+  specs = 0;
+  for (unsigned i = 0; i < ARRAY_SIZE (static_specs); i++)
+    {
+      spec_list *sl = &static_specs[i];
+      if (sl->alloc_p)
+ {
+  //free (const_cast <char *> (*(sl->ptr_spec)));
+  sl->alloc_p = false;
+ }
+      *(sl->ptr_spec) = sl->default_ptr;
+    }
+  extra_specs = NULL;
+
+  processing_spec_function = 0;
+
+  argbuf.truncate (0);
+
+  have_c = 0;
+  have_o = 0;
+
+  temp_names = NULL;
+  execution_count = 0;
+  signal_count = 0;
+
+  temp_filename = NULL;
+  temp_filename_length = 0;
+  always_delete_queue = NULL;
+  failure_delete_queue = NULL;
+
+  XDELETEVEC (switches);
+  switches = NULL;
+  n_switches = 0;
+  n_switches_alloc = 0;
+
+  compare_debug = 0;
+  compare_debug_second = 0;
+  compare_debug_opt = NULL;
+  for (int i = 0; i < 2; i++)
+    {
+      switches_debug_check[i] = NULL;
+      n_switches_debug_check[i] = 0;
+      n_switches_alloc_debug_check[i] = 0;
+      debug_check_temp_file[i] = NULL;
+    }
+
+  XDELETEVEC (infiles);
+  infiles = NULL;
+  n_infiles = 0;
+  n_infiles_alloc = 0;
+
+  combine_inputs = false;
+  added_libraries = 0;
+  XDELETEVEC (outfiles);
+  outfiles = NULL;
+  spec_lang = 0;
+  last_language_n_infiles = 0;
+  gcc_input_filename = NULL;
+  input_file_number = 0;
+  input_filename_length = 0;
+  basename_length = 0;
+  suffixed_basename_length = 0;
+  input_basename = NULL;
+  input_suffix = NULL;
+  /* We don't need to purge "input_stat", just to unset "input_stat_set".  */
+  input_stat_set = 0;
+  input_file_compiler = NULL;
+  arg_going = 0;
+  delete_this_arg = 0;
+  this_is_output_file = 0;
+  this_is_library_file = 0;
+  this_is_linker_script = 0;
+  input_from_pipe = 0;
+  suffix_subst = NULL;
+
+  mdswitches = NULL;
+  n_mdswitches = 0;
+
+  debug_auxbase_opt = NULL;
+
+  used_arg.finalize ();
+}
+
 /* PR jit/64810.
    Targets can provide configure-time default options in
    OPTION_DEFAULT_SPECS.  The jit needs to access these, but
diff --git a/gcc/gcc.h b/gcc/gcc.h
index f10a103..e1abe43 100644
--- a/gcc/gcc.h
+++ b/gcc/gcc.h
@@ -30,7 +30,10 @@ along with GCC; see the file COPYING3.  If not see
 class driver
 {
  public:
+  driver (bool can_finalize, bool debug);
+  ~driver ();
   int main (int argc, char **argv);
+  void finalize ();
 
  private:
   void set_progname (const char *argv0) const;
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 7a3b7f7..6282abd 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -2303,6 +2303,8 @@ convert_to_dso (const char *ctxt_progname)
  true);/* bool run_linker */
 }
 
+static const char * const gcc_driver_name = GCC_DRIVER_NAME;
+
 void
 playback::context::
 invoke_driver (const char *ctxt_progname,
@@ -2313,15 +2315,14 @@ invoke_driver (const char *ctxt_progname,
        bool run_linker)
 {
   JIT_LOG_SCOPE (get_logger ());
+
+  const bool embedded_driver = true;
+
   /* Currently this lumps together both assembling and linking into
      TV_ASSEMBLE.  */
   auto_timevar assemble_timevar (get_timer (), tv_id);
-  const char *errmsg;
   auto_vec <const char *> argvec;
 #define ADD_ARG(arg) argvec.safe_push (arg)
-  int exit_status = 0;
-  int err = 0;
-  const char *gcc_driver_name = GCC_DRIVER_NAME;
 
   ADD_ARG (gcc_driver_name);
 
@@ -2344,8 +2345,10 @@ invoke_driver (const char *ctxt_progname,
      time.  */
   ADD_ARG ("-fno-use-linker-plugin");
 
-  /* pex argv arrays are NULL-terminated.  */
-  ADD_ARG (NULL);
+  if (0)
+    ADD_ARG ("-v");
+
+#undef ADD_ARG
 
   /* pex_one's error-handling requires pname to be non-NULL.  */
   gcc_assert (ctxt_progname);
@@ -2354,9 +2357,40 @@ invoke_driver (const char *ctxt_progname,
     for (unsigned i = 0; i < argvec.length (); i++)
       get_logger ()->log ("argv[%i]: %s", i, argvec[i]);
 
+  if (embedded_driver)
+    invoke_embedded_driver (&argvec);
+  else
+    invoke_external_driver (ctxt_progname, &argvec);
+}
+
+void
+playback::context::
+invoke_embedded_driver (const vec <const char *> *argvec)
+{
+  driver d (true, /* can_finalize */
+    false); /* debug */
+  int result = d.main (argvec->length (),
+       const_cast <char **> (argvec->address ()));
+  d.finalize ();
+  if (result)
+    add_error (NULL, "error invoking gcc driver");
+}
+
+void
+playback::context::
+invoke_external_driver (const char *ctxt_progname,
+ vec <const char *> *argvec)
+{
+  const char *errmsg;
+  int exit_status = 0;
+  int err = 0;
+
+  /* pex argv arrays are NULL-terminated.  */
+  argvec->safe_push (NULL);
+
   errmsg = pex_one (PEX_SEARCH, /* int flags, */
     gcc_driver_name,
-    const_cast <char *const *> (argvec.address ()),
+    const_cast <char *const *> (argvec->address ()),
     ctxt_progname, /* const char *pname */
     NULL, /* const char *outname */
     NULL, /* const char *errname */
@@ -2383,7 +2417,6 @@ invoke_driver (const char *ctxt_progname,
  getenv ("PATH"));
       return;
     }
-#undef ADD_ARG
 }
 
 /* Dynamically-link the built DSO file into this process, using dlopen.
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index afe0068..7ee326b 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -281,6 +281,14 @@ protected:
   result *
   dlopen_built_dso ();
 
+ private:
+  void
+  invoke_embedded_driver (const vec <const char *> *argvec);
+
+  void
+  invoke_external_driver (const char *ctxt_progname,
+  vec <const char *> *argvec);
+
 private:
   ::gcc::jit::recording::context *m_recording_ctxt;
 
diff --git a/gcc/jit/notes.txt b/gcc/jit/notes.txt
index e92c665..36e05cb 100644
--- a/gcc/jit/notes.txt
+++ b/gcc/jit/notes.txt
@@ -78,7 +78,13 @@ Client Code   . Generated .            libgccjit.so
               .           .      │   .               .
               .           .      │   (assuming an in-memory compile):
               .           .      │   .               .
-              .           .      │   . Convert assembler to DSO ("fake.so")
+              .           .      --> Convert assembler to DSO, via embedded
+              .           .          copy of driver:
+              .           .           driver::main ()
+              .           .             invocation of "as"
+              .           .             invocation of "ld"
+              .           .           driver::finalize ()
+              .           .      <----
               .           .      │   .               .
               .           .      │   . Load DSO (dlopen "fake.so")
               .           .      │   .               .
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 14/16] gcc: Add CTIMER_PUSH/POP to gcc's copy of libiberty

David Malcolm
In reply to this post by David Malcolm
include/ChangeLog:
        * libiberty.h (struct ctimer): New.
        (CTIMER_PUSH): New.
        (CTIMER_POP): New.
---
 include/libiberty.h | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/include/libiberty.h b/include/libiberty.h
index b33dd65..a2c73ae 100644
--- a/include/libiberty.h
+++ b/include/libiberty.h
@@ -731,6 +731,38 @@ extern unsigned long libiberty_len;
    (char *) memcpy (libiberty_nptr, libiberty_optr, libiberty_len))
 #endif
 
+/* Support for hooking into gcc's timing mechanism
+   (class timer), from a pure C API, withough needing to link
+   against any symbols. */
+
+struct ctimer
+{
+   void (*push) (struct ctimer *t, const char *item_name);
+   void (*pop) (struct ctimer *t);
+};
+
+/* Macros for pushing/popping named timing items, so we can write e.g.:
+
+     CTIMER_PUSH (some_timer, "init");
+     init ();
+     CTIMER_POP ();
+
+   and have it redirected into GCC's timing mechanism.
+
+   Typically, CTIMER is NULL, and this does nothing.  */
+
+#define CTIMER_PUSH(CTIMER, ITEM_NAME)          \
+  do {                                          \
+    if (CTIMER)                                 \
+      (CTIMER)->push ((CTIMER), (ITEM_NAME));   \
+  } while (0)
+
+#define CTIMER_POP(CTIMER)                      \
+  do {                                          \
+    if (CTIMER)                                 \
+      (CTIMER)->pop (CTIMER);                   \
+  } while (0)
+
 #ifdef __cplusplus
 }
 #endif
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 03/16] gcc: Use timevars within driver

David Malcolm
In reply to this post by David Malcolm
This patch allows a "timer" instance to be passed to the driver
and adds some timevars for activities within the driver.

This is for use by libgccjit when embedding the driver, though
in theory we could use this to time the driver as a whole.

gcc/ChangeLog:
        * Makefile.in (GCC_OBJS): Add timevar.o.
        * gcc-main.c (main): Add NULL for "timer" when constructing
        the driver.
        * gcc.c: Include timevar.h.
        (driver::driver): Add timer param.
        (driver::main): Account various things to TV_DRIVER_SETUP.
        (driver::do_spec_on_infiles): Account time spent here to
        TV_DRIVER_SPEC.
        (driver::maybe_run_linker): Account time spent here to
        TV_DRIVER_LINK.
        * gcc.h: Add forward decl of class timer.
        (driver::driver): Add timer param.
        (driver::m_timer): New field.
        * timevar.def (TV_DRIVER): New.
        (TV_DRIVER_SETUP): New.
        (TV_DRIVER_SPEC): New.
        (TV_DRIVER_LINK): New.

gcc/jit/ChangeLog:
        * jit-playback.c (gcc:jit::playback::context::convert_to_dso):
        Account the driver to TV_DRIVER rather than to TV_ASSEMBLE.
        (gcc:jit::playback::context::invoke_embedded_driver): Pass
        the context's timer (if any) to the driver.
---
 gcc/Makefile.in        |  2 +-
 gcc/gcc-main.c         |  3 ++-
 gcc/gcc.c              | 14 ++++++++++++--
 gcc/gcc.h              |  5 ++++-
 gcc/jit/jit-playback.c |  5 +++--
 gcc/timevar.def        |  4 ++++
 6 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index ab9b637..2388975 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1140,7 +1140,7 @@ CXX_TARGET_OBJS=@cxx_target_objs@
 FORTRAN_TARGET_OBJS=@fortran_target_objs@
 
 # Object files for gcc many-languages driver.
-GCC_OBJS = gcc.o gcc-main.o ggc-none.o
+GCC_OBJS = gcc.o gcc-main.o ggc-none.o timevar.o
 
 c-family-warn = $(STRICT_WARN)
 
diff --git a/gcc/gcc-main.c b/gcc/gcc-main.c
index a0aaa3c..59533a5 100644
--- a/gcc/gcc-main.c
+++ b/gcc/gcc-main.c
@@ -41,7 +41,8 @@ int
 main (int argc, char **argv)
 {
   driver d (false, /* can_finalize */
-    false); /* debug */
+    false, /* debug */
+    NULL); /* timer */
 
   return d.main (argc, argv);
 }
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 683392d..7314317 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -43,6 +43,7 @@ compilation is specified by a string called a "spec".  */
 #include "params.h"
 #include "vec.h"
 #include "filenames.h"
+#include "timevar.h"
 
 
 
@@ -7023,9 +7024,10 @@ compare_files (char *cmpfile[])
   return ret;
 }
 
-driver::driver (bool can_finalize, bool debug) :
+driver::driver (bool can_finalize, bool debug, timer *t) :
   explicit_link_files (NULL),
-  decoded_options (NULL)
+  decoded_options (NULL),
+  m_timer (t)
 {
   env.init (can_finalize, debug);
 }
@@ -7043,6 +7045,9 @@ driver::main (int argc, char **argv)
 {
   bool early_exit;
 
+  if (m_timer)
+    m_timer->push (TV_DRIVER_SETUP);
+
   set_progname (argv[0]);
   expand_at_files (&argc, &argv);
   decode_argv (argc, const_cast <const char **> (argv));
@@ -7054,6 +7059,9 @@ driver::main (int argc, char **argv)
   maybe_putenv_OFFLOAD_TARGETS ();
   handle_unrecognized_options ();
 
+  if (m_timer)
+    m_timer->pop (TV_DRIVER_SETUP);
+
   if (!maybe_print_and_exit ())
     return 0;
 
@@ -7780,6 +7788,7 @@ driver::prepare_infiles ()
 void
 driver::do_spec_on_infiles () const
 {
+  auto_timevar tv (m_timer, TV_DRIVER_SPEC);
   size_t i;
 
   for (i = 0; (int) i < n_infiles; i++)
@@ -7926,6 +7935,7 @@ driver::do_spec_on_infiles () const
 void
 driver::maybe_run_linker (const char *argv0) const
 {
+  auto_timevar tv (m_timer, TV_DRIVER_LINK);
   size_t i;
   int linker_was_run = 0;
   int num_linker_inputs;
diff --git a/gcc/gcc.h b/gcc/gcc.h
index e1abe43..cfff8ef 100644
--- a/gcc/gcc.h
+++ b/gcc/gcc.h
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "version.h"
 #include "diagnostic-core.h"
 
+class timer;
+
 /* The top-level "main" within the driver would be ~1000 lines long.
    This class breaks it up into smaller functions and contains some
    state shared by them.  */
@@ -30,7 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 class driver
 {
  public:
-  driver (bool can_finalize, bool debug);
+  driver (bool can_finalize, bool debug, timer *t);
   ~driver ();
   int main (int argc, char **argv);
   void finalize ();
@@ -57,6 +59,7 @@ class driver
   char *explicit_link_files;
   struct cl_decoded_option *decoded_options;
   unsigned int decoded_options_count;
+  timer *m_timer;
 };
 
 /* The mapping of a spec function name to the C function that
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 6282abd..ff5a42f 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -2298,7 +2298,7 @@ convert_to_dso (const char *ctxt_progname)
   invoke_driver (ctxt_progname,
  m_tempdir->get_path_s_file (),
  m_tempdir->get_path_so_file (),
- TV_ASSEMBLE,
+ TV_DRIVER,
  true, /* bool shared, */
  true);/* bool run_linker */
 }
@@ -2368,7 +2368,8 @@ playback::context::
 invoke_embedded_driver (const vec <const char *> *argvec)
 {
   driver d (true, /* can_finalize */
-    false); /* debug */
+    false, /* debug */
+    get_timer ()); /* timer */
   int result = d.main (argvec->length (),
        const_cast <char **> (argvec->address ()));
   d.finalize ();
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 905b469..d04a729 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -286,7 +286,11 @@ DEFTIMEVAR (TV_REPAIR_LOOPS     , "repair loop structures")
 
 /* Stuff used by libgccjit.so.  */
 DEFTIMEVAR (TV_JIT_REPLAY     , "replay of JIT client activity")
+DEFTIMEVAR (TV_DRIVER                , "driver")
 DEFTIMEVAR (TV_ASSEMBLE     , "assemble JIT code")
+DEFTIMEVAR (TV_DRIVER_SETUP          , "driver: setup")
+DEFTIMEVAR (TV_DRIVER_SPEC     , "driver: do spec on infiles")
+DEFTIMEVAR (TV_DRIVER_LINK     , "driver: run linker")
 DEFTIMEVAR (TV_LINK     , "link JIT code")
 DEFTIMEVAR (TV_LOAD     , "load JIT result")
 DEFTIMEVAR (TV_JIT_ACQUIRING_MUTEX   , "acquiring JIT mutex")
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 12/16] binutils: Introduce "ld_main" and state-purging within "ld" subdir

David Malcolm
In reply to this post by David Malcolm
As before in "gas", introduce code to ld to restore state.

I've only tested this on x86_64, and there are some hacks in here
that hardcode that, but hopefully this is enough to show the idea.
---
 ld/emultempl/elf32.em |  77 +++++++++++++++++++++++++++++++++----
 ld/ld.h               |   8 ++++
 ld/ldexp.c            |  11 ++++++
 ld/ldlang.c           |  90 +++++++++++++++++++++++++++++++++++++++++++
 ld/ldmain.c           | 103 +++++++++++++++++++++++++++++++++++++++++++++-----
 ld/ldmainmain.c       |  34 +++++++++++++++++
 ld/lexsup.c           |  12 +++++-
 ld/libld.h            |  41 ++++++++++++++++++++
 8 files changed, 359 insertions(+), 17 deletions(-)
 create mode 100644 ld/ldmainmain.c
 create mode 100644 ld/libld.h

diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index 0802d76..e24987e 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -1740,12 +1740,9 @@ output_rel_find (asection *sec, int isdyn)
 /* Place an orphan section.  We use this to put random SHF_ALLOC
    sections in the right segment.  */
 
-static lang_output_section_statement_type *
-gld${EMULATION_NAME}_place_orphan (asection *s,
-   const char *secname,
-   int constraint)
-{
-  static struct orphan_save hold[] =
+static int orphan_init_done = 0;
+
+static struct orphan_save hold[] =
     {
       { ".text",
  SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
@@ -1775,6 +1772,12 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
  SEC_HAS_CONTENTS,
  0, 0, 0, 0 },
     };
+
+static lang_output_section_statement_type *
+gld${EMULATION_NAME}_place_orphan (asection *s,
+   const char *secname,
+   int constraint)
+{
   enum orphan_save_index
     {
       orphan_text = 0,
@@ -1787,7 +1790,6 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
       orphan_sdata,
       orphan_nonalloc
     };
-  static int orphan_init_done = 0;
   struct orphan_save *place;
   lang_output_section_statement_type *after;
   lang_output_section_statement_type *os;
@@ -2441,3 +2443,64 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL}
 };
 EOF
+
+fragment <<EOF
+
+/* FIXME: how to arrange for this to actually be called? */
+extern void
+gld${EMULATION_NAME}_finalize (void);
+
+void
+gld${EMULATION_NAME}_finalize (void)
+{
+  global_needed = NULL;
+  CLEAR_VAR (global_stat);
+  global_found = NULL;
+  global_vercheck_needed = NULL;
+  global_vercheck_failed = 0;
+  audit = NULL;
+  depaudit = NULL;
+  emit_note_gnu_build_id = NULL;
+  orphan_init_done = 0;
+
+  /* Reset the "hold" table.  */
+  CLEAR_VAR (hold);
+
+#define INIT_HOLD(IDX, NAME, FLAGS)   \
+  do {                                \
+    hold[IDX].name = (NAME);          \
+    hold[IDX].flags = (FLAGS);        \
+  } while (0)
+
+  INIT_HOLD (0,
+             ".text",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE);
+  INIT_HOLD (1,
+             ".rodata",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+  INIT_HOLD (2,
+             ".tdata",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_THREAD_LOCAL);
+  INIT_HOLD (3,
+             ".data",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA);
+  INIT_HOLD (4,
+             ".bss",
+             SEC_ALLOC);
+  INIT_HOLD (5,
+             NULL,
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+  INIT_HOLD (6,
+             ".interp",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+  INIT_HOLD (7,
+             ".sdata",
+             SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA);
+  INIT_HOLD (8,
+             ".comment",
+             SEC_HAS_CONTENTS);
+
+#undef INIT_HOLD
+}
+
+EOF
diff --git a/ld/ld.h b/ld/ld.h
index f804f9c..e114f45 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -302,4 +302,12 @@ extern void ld_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
 #undef abort
 #define abort() ld_abort (__FILE__, __LINE__, __PRETTY_FUNCTION__)
 
+extern struct ctimer *ld_timer;
+
+#define TIMER_PUSH(ITEM_NAME) \
+  do { CTIMER_PUSH (ld_timer, (ITEM_NAME)); } while (0)
+
+#define TIMER_POP() \
+  do { CTIMER_POP (ld_timer); } while (0)
+
 #endif
diff --git a/ld/ldexp.c b/ld/ldexp.c
index a5192b1..0c18cb5 100644
--- a/ld/ldexp.c
+++ b/ld/ldexp.c
@@ -1580,3 +1580,14 @@ ldexp_finish (void)
 {
   bfd_hash_table_free (&definedness_table);
 }
+
+/* FIXME.  */
+extern void ldexp_c_finalize (void);
+
+/* FIXME.  */
+void ldexp_c_finalize (void)
+{
+   segments = NULL;
+   CLEAR_VAR (expld);
+   CLEAR_VAR (definedness_table);
+}
diff --git a/ld/ldlang.c b/ld/ldlang.c
index c96c21f..d44bcab 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -2722,7 +2722,9 @@ load_symbols (lang_input_statement_type *entry,
   if (entry->flags.loaded)
     return TRUE;
 
+  TIMER_PUSH ("load_symbols: ldfile_open_file");
   ldfile_open_file (entry);
+  TIMER_POP ();
 
   /* Do not process further if the file was missing.  */
   if (entry->flags.missing_file)
@@ -2851,10 +2853,12 @@ load_symbols (lang_input_statement_type *entry,
       break;
     }
 
+  TIMER_PUSH ("load_symbols: bfd_link_add_symbols");
   if (bfd_link_add_symbols (entry->the_bfd, &link_info))
     entry->flags.loaded = TRUE;
   else
     einfo (_("%F%B: error adding symbols: %E\n"), entry->the_bfd);
+  TIMER_POP ();
 
   return entry->flags.loaded;
 }
@@ -3086,6 +3090,8 @@ lang_get_output_target (void)
 static void
 open_output (const char *name)
 {
+  TIMER_PUSH ("open_output");
+
   output_target = lang_get_output_target ();
 
   /* Has the user requested a particular endianness on the command
@@ -3161,6 +3167,8 @@ open_output (const char *name)
     einfo (_("%P%F: can not create hash table: %E\n"));
 
   bfd_set_gp_size (link_info.output_bfd, g_switch_value);
+
+  TIMER_POP ();
 }
 
 static void
@@ -3233,6 +3241,7 @@ static lang_input_statement_type *plugin_insert = NULL;
 static void
 open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
 {
+  TIMER_PUSH ("open_input_bfds");
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
@@ -3279,6 +3288,8 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
       lang_statement_list_type add;
       bfd *abfd;
 
+              TIMER_PUSH ("lang_input_statement_enum");
+
       s->input_statement.target = current_target;
 
       /* If we are being called from within a group, and this
@@ -3308,8 +3319,10 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
       os_tail = lang_output_section_statement.tail;
       lang_list_init (&add);
 
+              TIMER_PUSH ("open_input_bfds:load_symbols");
       if (! load_symbols (&s->input_statement, &add))
  config.make_executable = FALSE;
+              TIMER_POP ();
 
       if (add.head != NULL)
  {
@@ -3333,6 +3346,8 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
       s->header.next = add.head;
     }
  }
+
+              TIMER_POP ();
     }
 #ifdef ENABLE_PLUGINS
   /* If we have found the point at which a plugin added new
@@ -3354,6 +3369,8 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
   /* Exit if any of the files were missing.  */
   if (input_flags.missing_file)
     einfo ("%F");
+
+  TIMER_POP ();
 }
 
 /* Add the supplied name to the symbol table as an undefined reference.
@@ -6192,6 +6209,8 @@ lang_for_each_file (void (*func) (lang_input_statement_type *))
 void
 ldlang_add_file (lang_input_statement_type *entry)
 {
+  TIMER_PUSH ("ldlang_add_file");
+
   lang_statement_append (&file_chain,
  (lang_statement_union_type *) entry,
  &entry->next);
@@ -6217,6 +6236,8 @@ ldlang_add_file (lang_input_statement_type *entry)
      this, we should probably handle SEC_EXCLUDE in the same way.  */
 
   bfd_map_over_sections (entry->the_bfd, section_already_linked, entry);
+
+  TIMER_POP ();
 }
 
 void
@@ -6605,6 +6626,8 @@ lang_list_remove_tail (lang_statement_list_type *destlist,
 void
 lang_process (void)
 {
+  TIMER_PUSH ("lang_process: 1st half");
+
   /* Finalize dynamic list.  */
   if (link_info.dynamic_list)
     lang_finalize_version_expr_head (&link_info.dynamic_list->head);
@@ -6773,6 +6796,10 @@ lang_process (void)
  }
     }
 
+  TIMER_POP ();
+
+  TIMER_PUSH ("lang_process: 2nd half");
+
   /* Do anything special before sizing sections.  This is where ELF
      and other back-ends size dynamic sections.  */
   ldemul_before_allocation ();
@@ -6806,6 +6833,8 @@ lang_process (void)
     lang_check_section_addresses ();
 
   lang_end ();
+
+  TIMER_POP ();
 }
 
 /* EXPORTED TO YACC */
@@ -8133,3 +8162,64 @@ lang_ld_feature (char *str)
       p = q;
     }
 }
+
+/* FIXME.  */
+extern void ldlang_c_finalize (void);
+
+/* FIXME.  */
+void ldlang_c_finalize (void)
+{
+  obstack_free (&stat_obstack, 0);
+  CLEAR_VAR (stat_obstack);
+  obstack_free (&map_obstack, 0);
+  CLEAR_VAR (map_obstack);
+  entry_symbol_default = "start";
+  placed_commons = FALSE;
+  map_head_is_link_order = FALSE;
+  default_common_section = NULL;
+  map_option_f = 0;
+  print_dot = 0;
+  first_file = NULL;
+  current_target = NULL;
+  CLEAR_VAR (statement_list);
+  CLEAR_VAR (stat_save);
+  stat_save_ptr = &stat_save[0];
+  unique_section_list = NULL;
+  asneeded_list_head = NULL;
+  output_target = NULL;
+  abs_output_section = NULL;
+  CLEAR_VAR (lang_output_section_statement);
+  stat_ptr = &statement_list;
+  CLEAR_VAR (file_chain);
+  CLEAR_VAR (input_file_chain);
+  CLEAR_VAR (entry_symbol);
+  entry_section = ".text";
+  CLEAR_VAR (input_flags);
+  entry_from_cmdline = 0;
+  undef_from_cmdline = 0;
+  lang_has_input_file = FALSE;
+  had_output_filename = FALSE;
+  lang_float_flag = FALSE;
+  delete_output_file_on_failure = FALSE;
+  lang_phdr_list = NULL;
+  nocrossref_list = NULL;
+  asneeded_list_tail = NULL;
+  lang_statement_iteration = 0;
+  CLEAR_VAR (output_section_statement_table);
+  lang_memory_region_list = NULL;
+  lang_memory_region_list_tail = &lang_memory_region_list;
+  excluded_libs = NULL;
+  winner = NULL;
+  opb_shift = 0;
+#ifdef ENABLE_PLUGINS
+  plugin_insert = NULL;
+#endif
+  current_section = NULL;
+  current_assign = NULL;
+  prefer_next_section = 0;
+  overlay_vma = NULL;
+  overlay_subalign = NULL;
+  overlay_max = NULL;
+  overlay_list = NULL;
+  version_index = 0;
+}
diff --git a/ld/ldmain.c b/ld/ldmain.c
index a7b72bd..daa4903 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -27,6 +27,8 @@
 #include "bfdlink.h"
 #include "filenames.h"
 
+#include "libld.h"
+
 #include "ld.h"
 #include "ldmain.h"
 #include "ldmisc.h"
@@ -138,7 +140,7 @@ static bfd_boolean notice
   (struct bfd_link_info *, struct bfd_link_hash_entry *,
    struct bfd_link_hash_entry *, bfd *, asection *, bfd_vma, flagword);
 
-static struct bfd_link_callbacks link_callbacks =
+static const struct bfd_link_callbacks link_callbacks =
 {
   add_archive_element,
   multiple_definition,
@@ -183,8 +185,10 @@ ld_bfd_assert_handler (const char *fmt, const char *bfdver,
   config.make_executable = FALSE;
 }
 
-int
-main (int argc, char **argv)
+/* FIXME.  */
+
+static int
+ld_internal_main (int argc, char **argv, int standalone)
 {
   char *emulation;
   long start_time = get_run_time ();
@@ -192,6 +196,8 @@ main (int argc, char **argv)
   char *start_sbrk = (char *) sbrk (0);
 #endif
 
+  TIMER_PUSH ("ld_internal_main: init");
+
 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
   setlocale (LC_MESSAGES, "");
 #endif
@@ -217,7 +223,8 @@ main (int argc, char **argv)
      leave no trace.  */
   default_bfd_assert_handler = bfd_set_assert_handler (ld_bfd_assert_handler);
 
-  xatexit (ld_cleanup);
+  if (standalone)
+    xatexit (ld_cleanup);
 
   /* Set up the sysroot directory.  */
   ld_sysroot = get_sysroot (argc, argv);
@@ -236,6 +243,8 @@ main (int argc, char **argv)
   else
     ld_canon_sysroot_len = -1;
 
+  TIMER_POP ();
+
   /* Set the default BFD target based on the configured target.  Doing
      this permits the linker to be configured for a particular target,
      and linked against a shared BFD library which was configured for
@@ -243,7 +252,7 @@ main (int argc, char **argv)
   if (! bfd_set_default_target (TARGET))
     {
       einfo (_("%X%P: can't set BFD default target to `%s': %E\n"), TARGET);
-      xexit (1);
+      return 1;
     }
 
 #if YYDEBUG
@@ -377,19 +386,21 @@ main (int argc, char **argv)
   if (command_line.print_output_format)
     info_msg ("%s\n", lang_get_output_target ());
 
+  TIMER_PUSH ("ldmain.c: lang_final");
   lang_final ();
+  TIMER_POP ();
 
   /* If the only command line argument has been -v or --version or --verbose
      then ignore any input files provided by linker scripts and exit now.
      We do not want to create an output file when the linker is just invoked
      to provide version information.  */
   if (argc == 2 && version_printed)
-    xexit (0);
+    return 0;
 
   if (!lang_has_input_file)
     {
       if (version_printed || command_line.print_output_format)
- xexit (0);
+ return 0;
       einfo (_("%P%F: no input files\n"));
     }
 
@@ -416,7 +427,9 @@ main (int argc, char **argv)
  }
     }
 
+  TIMER_PUSH ("ldmain.c: lang_process");
   lang_process ();
+  TIMER_POP ();
 
   /* Print error messages for any missing symbols, for any warning
      symbols, and possibly multiple definitions.  */
@@ -432,7 +445,9 @@ main (int argc, char **argv)
  link_info.output_bfd->flags |= BFD_COMPRESS_GABI;
     }
 
+  TIMER_PUSH ("ldmain.c: ldwrite");
   ldwrite ();
+  TIMER_POP ();
 
   if (config.map_file != NULL)
     lang_map ();
@@ -461,7 +476,7 @@ main (int argc, char **argv)
        output_filename);
 
       /* The file will be removed by ld_cleanup.  */
-      xexit (1);
+      return 1;
     }
   else
     {
@@ -536,10 +551,56 @@ main (int argc, char **argv)
   /* Prevent ld_cleanup from doing anything, after a successful link.  */
   output_filename = NULL;
 
-  xexit (0);
   return 0;
 }
 
+/* FIXME. */
+extern void ldexp_c_finalize (void);
+extern void ldlang_c_finalize (void);
+
+static void ldmain_c_finalize (void);
+
+/* FIXME: hardcoded; really ought to be calling all of these.  */
+extern void
+gldelf_x86_64_finalize (void);
+
+/* The global ctimer instance (or NULL).  */
+struct ctimer *ld_timer;
+
+/* FIXME. */
+int
+ld_main (int argc, char **argv, int standalone, struct ctimer *timer)
+{
+  int result;
+
+  /* Set global ld_timer.  */
+  ld_timer = timer;
+
+  result = ld_internal_main (argc, argv, standalone);
+  if (!standalone)
+    {
+      TIMER_PUSH ("ld_main cleanup");
+      ld_cleanup ();
+
+      /* TODO.  */
+      bfd_uninit ();
+
+      ldexp_c_finalize ();
+      ldlang_c_finalize ();
+      ldmain_c_finalize ();
+
+      /* FIXME: hardcoded.  */
+      gldelf_x86_64_finalize ();
+
+      TIMER_POP ();
+    }
+
+  /* Unset global ld_timer.  */
+  ld_timer = NULL;
+
+  return result;
+}
+
 /* If the configured sysroot is relocatable, try relocating it based on
    default prefix FROM.  Return the relocated directory if it exists,
    otherwise return null.  */
@@ -1486,3 +1547,27 @@ notice (struct bfd_link_info *info,
 
   return TRUE;
 }
+
+/* FIXME.  */
+static void ldmain_c_finalize (void)
+{
+  saved_script_handle = NULL;
+  previous_script_handle = NULL;
+  force_make_executable = FALSE;
+  default_target = NULL;
+  output_filename = "a.out";
+  program_name = NULL;
+  ld_sysroot = NULL;
+  ld_canon_sysroot = NULL;
+  ld_canon_sysroot_len = 0;
+  g_switch_value = 8;
+  trace_files = 0;
+  verbose = 0;
+  version_printed = 0;
+  CLEAR_VAR (command_line);
+  CLEAR_VAR (config);
+  sort_section = 0;
+  default_bfd_assert_handler = NULL;
+  CLEAR_VAR (link_info);
+  overflow_cutoff_limit = 10;
+}
diff --git a/ld/ldmainmain.c b/ld/ldmainmain.c
new file mode 100644
index 0000000..d897cd8
--- /dev/null
+++ b/ld/ldmainmain.c
@@ -0,0 +1,34 @@
+/* The entrypoint to the GNU linker.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program 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 of the License, or
+   (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "safe-ctype.h"
+#include "libiberty.h"
+#include "libld.h"
+
+/* FIXME.  */
+
+int
+main (int argc, char **argv)
+{
+  int result = ld_main (argc, argv, 1, NULL); /* standalone */
+  xexit (result);
+}
diff --git a/ld/lexsup.c b/ld/lexsup.c
index b618241..9cac4a7 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -527,6 +527,8 @@ static const struct ld_option ld_options[] =
 
 #define OPTION_COUNT ARRAY_SIZE (ld_options)
 
+static unsigned int defsym_count;
+
 void
 parse_args (unsigned argc, char **argv)
 {
@@ -635,12 +637,16 @@ parse_args (unsigned argc, char **argv)
  }
     }
 
+  /* Reset internal state of getopt_long_only.  */
+  optind = 0;
+
+  defsym_count = 0;
+
   last_optind = -1;
   while (1)
     {
       int longind;
       int optc;
-      static unsigned int defsym_count;
 
       /* Using last_optind lets us avoid calling ldemul_parse_args
  multiple times on a single option, which would lead to
@@ -1584,6 +1590,10 @@ parse_args (unsigned argc, char **argv)
       if (link_info.discard == discard_sec_merge)
  link_info.discard = discard_all;
     }
+
+  free (shortopts);
+  free (longopts);
+  free (really_longopts);
 }
 
 /* Add the (colon-separated) elements of DIRLIST_PTR to the
diff --git a/ld/libld.h b/ld/libld.h
new file mode 100644
index 0000000..9604ac9
--- /dev/null
+++ b/ld/libld.h
@@ -0,0 +1,41 @@
+/* API for the GNU linker.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program 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 of the License, or
+   (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#ifndef LIBLD_H
+#define LIBLD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ctimer;
+
+extern int
+ld_main (int argc,
+         char **argv,
+         int standalone,
+         struct ctimer *timer);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* ! defined (LIBLD_H) */
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 13/16] ld/Makefile.am: Introduce a libld.la

David Malcolm
In reply to this post by David Malcolm
Similarly to "libgas.la" before , this introduces a libld.la, moving
everything from "ld" into it, with ld built from ldmainmain.c.

As before with the libgas.la patch, this patch isn't ready yet, but
seems to be good enough for prototyping the libgccjit.so integration.
---
 ld/Makefile.am | 64 ++++++++++++++++++++++++----------------------------------
 1 file changed, 26 insertions(+), 38 deletions(-)

diff --git a/ld/Makefile.am b/ld/Makefile.am
index 55b62c5..71e4f8a 100644
--- a/ld/Makefile.am
+++ b/ld/Makefile.am
@@ -40,6 +40,7 @@ ELF_CLFAGS=-DELF_LIST_OPTIONS=@elf_list_options@ \
 WARN_CFLAGS = @WARN_CFLAGS@
 NO_WERROR = @NO_WERROR@
 AM_CFLAGS = $(WARN_CFLAGS) $(ELF_CLFAGS)
+CFLAGS +=-fPIC
 
 # Conditionally enable the plugin interface.
 if ENABLE_PLUGINS
@@ -132,11 +133,14 @@ CXX_FOR_TARGET = ` \
 
 transform = s/^ld-new$$/$(installed_linker)/;@program_transform_name@
 bin_PROGRAMS = ld-new
+lib_LTLIBRARIES = libld.la
 info_TEXINFOS = ld.texinfo
 ld_TEXINFOS = configdoc.texi
 noinst_TEXINFOS = ldint.texinfo
 man_MANS = ld.1
 
+include_HEADERS = libld.h
+
 AM_MAKEINFOFLAGS = -I $(srcdir) -I $(BFDDIR)/doc -I ../bfd/doc \
    -I $(top_srcdir)/../libiberty
 TEXI2DVI = texi2dvi -I $(srcdir) -I $(BFDDIR)/doc -I ../bfd/doc \
@@ -583,42 +587,23 @@ po/POTFILES.in: @MAINT@ Makefile
  for f in $(POTFILES); do echo $$f; done | LC_ALL=C sort > tmp \
   && mv tmp $(srcdir)/po/POTFILES.in
 
-ldmain.@OBJEXT@: ldmain.c config.status
-if am__fastdepCC
- $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ \
+# FIXME:
+#   ldmain.lo wants:
+#     -DDEFAULT_EMULATION='"$(EMUL)"' \
+#     -DBINDIR='"$(bindir)"' -DTOOLBINDIR='"$(tooldir)/bin"' \
+#     -DTARGET='"@target@"' @TARGET_SYSTEM_ROOT_DEFINE@
+#
+#   ldfile.lo wants:
+#     -DSCRIPTDIR='"$(scriptdir)"' -DBINDIR='"$(bindir)"' -DTOOLBINDIR='"$(tooldir)/bin"'
+#
+# Here we combine them all:
+AM_CFLAGS += \
   -DDEFAULT_EMULATION='"$(EMUL)"' \
   -DBINDIR='"$(bindir)"' -DTOOLBINDIR='"$(tooldir)/bin"' \
   -DTARGET='"@target@"' @TARGET_SYSTEM_ROOT_DEFINE@ \
-  $(srcdir)/ldmain.c
- mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-else
-if AMDEP
- source='ldmain.c' object='$@' libtool=no @AMDEPBACKSLASH@
- DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-endif
- $(COMPILE) -c -DDEFAULT_EMULATION='"$(EMUL)"' \
-  -DBINDIR='"$(bindir)"' -DTOOLBINDIR='"$(tooldir)/bin"' \
-  -DTARGET='"@target@"' @TARGET_SYSTEM_ROOT_DEFINE@ \
-  $(srcdir)/ldmain.c
-endif
+  -DSCRIPTDIR='"$(scriptdir)"'
 
-ldfile.@OBJEXT@: ldfile.c config.status
-if am__fastdepCC
- $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ \
- -DSCRIPTDIR='"$(scriptdir)"' -DBINDIR='"$(bindir)"' -DTOOLBINDIR='"$(tooldir)/bin"' \
- $(srcdir)/ldfile.c
- mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-else
-if AMDEP
- source='ldfile.c' object='$@' libtool=no @AMDEPBACKSLASH@
- DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-endif
- $(COMPILE) -c -DSCRIPTDIR='"$(scriptdir)"' -DBINDIR='"$(bindir)"' \
-  -DTOOLBINDIR='"$(tooldir)/bin"' \
-  $(srcdir)/ldfile.c
-endif
-
-eelf32_spu.@OBJEXT@: eelf32_spu.c
+eelf32_spu.lo: eelf32_spu.c
 if am__fastdepCC
  $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ \
   -DEMBEDSPU="\"`echo embedspu | sed '$(transform)'`\"" eelf32_spu.c
@@ -1999,19 +1984,22 @@ eshlelf64_nbsd.c: $(srcdir)/emulparams/shlelf64_nbsd.sh \
 
 
 # We need this for automake to use YLWRAP.
-EXTRA_ld_new_SOURCES = deffilep.y ldlex.l
+EXTRA_libld_la_SOURCES = deffilep.y ldlex.l
 # Allow dependency tracking to work for these files, too.
-EXTRA_ld_new_SOURCES += pep-dll.c pe-dll.c
+EXTRA_libld_la_SOURCES += pep-dll.c pe-dll.c
+
+ld_new_SOURCES = ldmainmain.c
 
-ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
+libld_la_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
  ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C) \
  ldbuildid.c
-ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
+libld_la_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
       $(BFDLIB) $(LIBIBERTY) $(LIBINTL_DEP)
-ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBIBERTY) $(LIBINTL)
+libld_la_LIBADD =  $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBIBERTY) $(LIBINTL)
+ld_new_LDADD = libld.la
 
 # Dependency tracking for the generated emulation files.
-EXTRA_ld_new_SOURCES += $(ALL_EMULATION_SOURCES) $(ALL_64_EMULATION_SOURCES)
+EXTRA_libld_la_SOURCES += $(ALL_EMULATION_SOURCES) $(ALL_64_EMULATION_SOURCES)
 
 # This is the real libbfd.a created by libtool.
 TESTBFDLIB = @TESTBFDLIB@
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

[PATCH 16/16] gcc: Hack up the arguments to the linker

David Malcolm
In reply to this post by David Malcolm
This is a nasty hack, but I noticed a lot of time spent executing the
linker within jit.dg/test-benchmark, with link lines like:

  run_embedded_ld: 24 args
    argv[0]: ld
    argv[1]: --eh-frame-hdr
    argv[2]: -m
    argv[3]: elf_x86_64
    argv[4]: -shared
    argv[5]: -o
    argv[6]: /tmp/libgccjit-mxDkPa/fake.so
    argv[7]: /lib/../lib64/crti.o
    argv[8]: /home/david/coding-3/gcc-git-jit-benchmarks/build-jit/gcc/crtbeginS.o
    argv[9]: -L/lib/../lib64
    argv[10]: -L/usr/lib/../lib64
    argv[11]: -L/home/david/coding-3/gcc-git-jit-benchmarks/build-jit/gcc
    argv[12]: /tmp/ccSlluGf.o
    argv[13]: -lgcc
    argv[14]: --as-needed
    argv[15]: -lgcc_s
    argv[16]: --no-as-needed
    argv[17]: -lc
    argv[18]: -lgcc
    argv[19]: --as-needed
    argv[20]: -lgcc_s
    argv[21]: --no-as-needed
    argv[22]: /home/david/coding-3/gcc-git-jit-benchmarks/build-jit/gcc/crtendS.o
    argv[23]: /lib/../lib64/crtn.o

Much of this appears to be unnecessary; if I purge the link line to
just:

    argv[0]: ld
    argv[1]: --eh-frame-hdr
    argv[2]: -m
    argv[3]: elf_x86_64
    argv[4]: -shared
    argv[5]: -o
    argv[6]: /tmp/libgccjit-mxDkPa/fake.so
    argv[7]: /tmp/ccSlluGf.o

the benchmark gets a 2x speedup.

Clearly this patch isn't appropriate as-is; when are the various
objects/libraries above that I hacked out actually needed?  Is there
a way programmatically determine what's needed?

gcc/ChangeLog:
        * gcc.c (purge_ld_arg): HACK! Purge the arguments to the
        linker.
        (run_embedded_ld): Likewise.
---
 gcc/gcc.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/gcc/gcc.c b/gcc/gcc.c
index ed92c7d..dc036e8 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -2887,6 +2887,25 @@ static int run_embedded_as (int argc, const char **argv)
 
 #ifdef HAVE_LIBLD
 
+/* HACK!  */
+static bool
+purge_ld_arg (const char *arg)
+{
+  /* Hack away "-l", e.g. -lgcc, -lgcc_s, -lc.  */
+  if (0 == strncmp (arg, "-l", 2))
+    return true;
+
+  /* If we can survive without -l, we don't need -L.  */
+  if (0 == strncmp (arg, "-L", 2))
+    return true;
+
+  /* Hack away crti.o, crtbeginS.o, crtendS.o, crtn.o.  */
+  if (strstr (arg, "/crt"))
+    return true;
+
+  return false;
+}
+
 /* Invoke ld_main, passing in the driver's timer
    so that the linker code can record timing information into it.  */
 
@@ -2902,6 +2921,29 @@ static int run_embedded_ld (int argc, const char **argv)
  fprintf (stderr, "  argv[%i]: %s\n", i, argv[i]);
     }
 
+  /* FIXME: Hack.  Purge the linker line, to see if we can speed things
+     up.   */
+  int idx = 0;
+  while (idx < argc)
+    {
+      if (purge_ld_arg (argv[idx]))
+ {
+  memmove (&argv[idx], &argv[idx + 1],
+   sizeof (char *) * (argc - (idx + 1)));
+  argc--;
+ }
+      else
+ idx++;
+    }
+
+  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,
--
1.8.5.3

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 09/16] libiberty.h: Provide CTIMER_PUSH/POP

DJ Delorie-2
In reply to this post by David Malcolm

libiberty is not an API to gcc, it is a portability library.  If GCC
is exporting a timer, GCC's headers should have the interface in it.
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 14/16] gcc: Add CTIMER_PUSH/POP to gcc's copy of libiberty

DJ Delorie-2
In reply to this post by David Malcolm

This is the same patch as 09/16.  There is only one libiberty master,
controlled by gcc, it is not neccessary to submit separate patches for
each copy of it.

The convention is: Any libiberty patch approved by gcc maintainers is
auto-approved for the other repos.
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 08/16] libiberty.h: Provide a CLEAR_VAR macro

DJ Delorie-2
In reply to this post by David Malcolm

> +/* Fill an lvalue with zero bits.  */
> +#define CLEAR_VAR(S) \
> +  do { memset (&(S), 0, sizeof (S)); } while (0)

Hmmm... I don't see the point in this.  The macro is almost as long as
a bare memset() would be, and replaces a well-known function with
something unknown outside this project.  It neither hides a bug in an
OS nor provides a common way to handle a difficult task across
platforms.

You also do NOT want to use memset to zero out a C++ structure that
contains more than just "plain old data" - you could easily corrupt
the structure's hidden data.

Also, pedantically speaking, "all bits zero" is not guaranteed to be
the same as float 0.0, double 0.0, or pointer (void *)0.
12