diff mbox

[3/4] Extract deferred-location handling from jit

Message ID 1462394970-55471-4-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm May 4, 2016, 8:49 p.m. UTC
In order to faithfully load RTL dumps that contain references to
source locations, the RTL frontend needs to be able to parse file
and line information and turn then into location_t values.

Unfortunately, the libcpp API makes it rather fiddly to create
location_t values from a sequence of arbitrary file/line pairs: the
API assumes that the locations are created in ascending order as
if we were parsing the source file, but as we read an RTL dump,
the insns could be jumping forwards and backwards in lines and
between files.  Also, if we want to support column numbers, the
presence of a very high column number could exceed the bits available
in a line_map_ordinary for storing it.

The JIT has some code for handling this, in gcc/jit/jit-playback.[ch],
(since the JIT support source location information, and doesn't impose
any ordering requirement on users of the API).

This patch moves the relevant code from
  gcc/jit/jit-playback.[ch]
into a new pair of files:
  gcc/deferred-locations.[ch]

The idea is that a deferred_locations instances manages these
"deferred locations"; they are created, and then all of the location_t
values are created at once by calling
  deferred_locations::add_to_line_table
After this call, the actual location_t values can be read from out of
deferred_location instances.

There are some suboptimal parts of the code (some linear searches, and
the use of gc), but it's mostly a move of existing code from out of the
jit subdirectory and into "gcc" proper for reuse by the RTL frontend.

This is likely to be useful for the gimple frontend as well.

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add deferred-locations.o.
	* deferred-locations.c: New file, adapted from parts of
	jit/jit-playback.c.
	* deferred-locations.h: New file, adapted from parts of
	jit/jit-playback.h.

gcc/jit/ChangeLog:
	* jit-common.h: Include deferred-locations.h.
	(gcc::jit::playback::source_file): Remove forward decl.
	(gcc::jit::playback::source_line): Likewise.
	(gcc::jit::playback::location): Replace forward decl, with
	a typedef, aliasing deferred_location.
	* jit-playback.c (gcc::jit::playback::context::context): Remove
	create call on m_source_files.
	(line_comparator): Move to deferred-locations.c.
	(location_comparator): Likewise.
	(handle_locations): Move logic to deferred-locations.c, as
	deferred_locations::add_to_line_table.
	(get_recording_loc): New function.
	(gcc::jit::playback::context::add_error): Call get_recording_loc
	as a function, rather than as a method.
	(gcc::jit::playback::context::add_error_va): Likewise.
	(gcc::jit::playback::context::get_source_file): Update return type
	to reflect move of source_file to deferred-locations.h.
	Replace body with a call to m_deferred_locations.get_source_file.
	(gcc::jit::playback::source_file::source_file): Move to
	deferred-locations.h, losing the namespaces.
	(gcc::jit::playback::source_file::finalizer): Likewise.
	(gcc::jit::playback::source_file::get_source_line): Likewise.
	(gcc::jit::playback::source_line::source_line): Likewise.
	(gcc::jit::playback::source_line::finalizer): Likewise.
	(gcc::jit::playback::source_line::get_location): Likewise.
	(gcc::jit::playback::location::location): Likewise, renaming to
	deferred_location.
	* jit-playback.h: Include deferred-locations.h.
	(gcc::jit::playback::context::m_source_files): Replace field with
	m_deferred_locations.
	(gcc::jit::playback::source_file): Move to deferred-locations.h,
	losing the namespaces.
	(gcc::jit::playback::source_line): Likewise.
	(gcc::jit::playback::location): Likewise, renaming to
	deferred_location.  Eliminate get_recording_loc accessor and
	m_recording_loc field in favor of get_user_data and m_user_data
	respectively.
---
 gcc/Makefile.in          |   1 +
 gcc/deferred-locations.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/deferred-locations.h | 139 +++++++++++++++++++++++++++
 gcc/jit/jit-common.h     |   5 +-
 gcc/jit/jit-playback.c   | 194 ++++----------------------------------
 gcc/jit/jit-playback.h   |  73 +-------------
 6 files changed, 402 insertions(+), 250 deletions(-)
 create mode 100644 gcc/deferred-locations.c
 create mode 100644 gcc/deferred-locations.h

Comments

Richard Biener May 6, 2016, 10:36 a.m. UTC | #1
On Wed, May 4, 2016 at 10:49 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> In order to faithfully load RTL dumps that contain references to
> source locations, the RTL frontend needs to be able to parse file
> and line information and turn then into location_t values.
>
> Unfortunately, the libcpp API makes it rather fiddly to create
> location_t values from a sequence of arbitrary file/line pairs: the
> API assumes that the locations are created in ascending order as
> if we were parsing the source file, but as we read an RTL dump,
> the insns could be jumping forwards and backwards in lines and
> between files.  Also, if we want to support column numbers, the
> presence of a very high column number could exceed the bits available
> in a line_map_ordinary for storing it.
>
> The JIT has some code for handling this, in gcc/jit/jit-playback.[ch],
> (since the JIT support source location information, and doesn't impose
> any ordering requirement on users of the API).
>
> This patch moves the relevant code from
>   gcc/jit/jit-playback.[ch]
> into a new pair of files:
>   gcc/deferred-locations.[ch]
>
> The idea is that a deferred_locations instances manages these
> "deferred locations"; they are created, and then all of the location_t
> values are created at once by calling
>   deferred_locations::add_to_line_table
> After this call, the actual location_t values can be read from out of
> deferred_location instances.
>
> There are some suboptimal parts of the code (some linear searches, and
> the use of gc), but it's mostly a move of existing code from out of the
> jit subdirectory and into "gcc" proper for reuse by the RTL frontend.
>
> This is likely to be useful for the gimple frontend as well.
>
> OK for trunk?

The LTO FE faces similar issues - see lto-streamer-in.c

Richard.

> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add deferred-locations.o.
>         * deferred-locations.c: New file, adapted from parts of
>         jit/jit-playback.c.
>         * deferred-locations.h: New file, adapted from parts of
>         jit/jit-playback.h.
>
> gcc/jit/ChangeLog:
>         * jit-common.h: Include deferred-locations.h.
>         (gcc::jit::playback::source_file): Remove forward decl.
>         (gcc::jit::playback::source_line): Likewise.
>         (gcc::jit::playback::location): Replace forward decl, with
>         a typedef, aliasing deferred_location.
>         * jit-playback.c (gcc::jit::playback::context::context): Remove
>         create call on m_source_files.
>         (line_comparator): Move to deferred-locations.c.
>         (location_comparator): Likewise.
>         (handle_locations): Move logic to deferred-locations.c, as
>         deferred_locations::add_to_line_table.
>         (get_recording_loc): New function.
>         (gcc::jit::playback::context::add_error): Call get_recording_loc
>         as a function, rather than as a method.
>         (gcc::jit::playback::context::add_error_va): Likewise.
>         (gcc::jit::playback::context::get_source_file): Update return type
>         to reflect move of source_file to deferred-locations.h.
>         Replace body with a call to m_deferred_locations.get_source_file.
>         (gcc::jit::playback::source_file::source_file): Move to
>         deferred-locations.h, losing the namespaces.
>         (gcc::jit::playback::source_file::finalizer): Likewise.
>         (gcc::jit::playback::source_file::get_source_line): Likewise.
>         (gcc::jit::playback::source_line::source_line): Likewise.
>         (gcc::jit::playback::source_line::finalizer): Likewise.
>         (gcc::jit::playback::source_line::get_location): Likewise.
>         (gcc::jit::playback::location::location): Likewise, renaming to
>         deferred_location.
>         * jit-playback.h: Include deferred-locations.h.
>         (gcc::jit::playback::context::m_source_files): Replace field with
>         m_deferred_locations.
>         (gcc::jit::playback::source_file): Move to deferred-locations.h,
>         losing the namespaces.
>         (gcc::jit::playback::source_line): Likewise.
>         (gcc::jit::playback::location): Likewise, renaming to
>         deferred_location.  Eliminate get_recording_loc accessor and
>         m_recording_loc field in favor of get_user_data and m_user_data
>         respectively.
> ---
>  gcc/Makefile.in          |   1 +
>  gcc/deferred-locations.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/deferred-locations.h | 139 +++++++++++++++++++++++++++
>  gcc/jit/jit-common.h     |   5 +-
>  gcc/jit/jit-playback.c   | 194 ++++----------------------------------
>  gcc/jit/jit-playback.h   |  73 +-------------
>  6 files changed, 402 insertions(+), 250 deletions(-)
>  create mode 100644 gcc/deferred-locations.c
>  create mode 100644 gcc/deferred-locations.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 6c5adc0..c61f303 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1239,6 +1239,7 @@ OBJS = \
>         dce.o \
>         ddg.o \
>         debug.o \
> +       deferred-locations.o \
>         df-core.o \
>         df-problems.o \
>         df-scan.o \
> diff --git a/gcc/deferred-locations.c b/gcc/deferred-locations.c
> new file mode 100644
> index 0000000..cd02938
> --- /dev/null
> +++ b/gcc/deferred-locations.c
> @@ -0,0 +1,240 @@
> +/* Dealing with the linemap API.
> +   Copyright (C) 2013-2016 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "input.h"
> +#include "deferred-locations.h"
> +#include "stringpool.h"
> +#include "tree.h"
> +
> +/* The wrapper subclasses are GC-managed, but can own non-GC memory.
> +   Provide this finalization hook for calling then they are collected,
> +   which calls the finalizer vfunc.  This allows them to call "release"
> +   on any vec<> within them.  */
> +
> +static void
> +wrapper_finalizer (void *ptr)
> +{
> +  wrapper *w = reinterpret_cast <wrapper *> (ptr);
> +  w->finalizer ();
> +}
> +
> +/* wrapper subclasses are GC-managed:
> +   allocate them using ggc_internal_cleared_alloc.  */
> +
> +void *
> +wrapper::operator new (size_t sz)
> +{
> +  return ggc_internal_cleared_alloc (sz, wrapper_finalizer, 0, 1);
> +
> +}
> +
> +/* Constructor for source_file.  */
> +
> +source_file::source_file (tree filename) :
> +  m_source_lines (),
> +  m_filename (filename)
> +{
> +}
> +
> +/* Don't leak vec's internal buffer (in non-GC heap) when we are
> +   GC-ed.  */
> +
> +void
> +source_file::finalizer ()
> +{
> +  m_source_lines.release ();
> +}
> +
> +/* Construct a source_line for the given line
> +   within this source file, if one doesn't exist already.  */
> +
> +source_line *
> +source_file::get_source_line (int line_num)
> +{
> +  /* Locate the line.
> +     For simplicitly, this is currently a linear search.
> +     Replace with a hash if this shows up in the profile.  */
> +  int i;
> +  source_line *line;
> +
> +  FOR_EACH_VEC_ELT (m_source_lines, i, line)
> +    if (line->get_line_num () == line_num)
> +      return line;
> +
> +  /* Not found.  */
> +  line = new source_line (this, line_num);
> +  m_source_lines.safe_push (line);
> +  return line;
> +}
> +
> +const char *
> +source_file::get_filename () const
> +{
> +  return IDENTIFIER_POINTER (m_filename);
> +}
> +
> +/* Constructor for source_line.  */
> +
> +source_line::source_line (source_file *file, int line_num) :
> +  m_locations (),
> +  m_source_file (file),
> +  m_line_num (line_num)
> +{
> +}
> +
> +/* Don't leak vec's internal buffer (in non-GC heap) when we are
> +   GC-ed.  */
> +
> +void
> +source_line::finalizer ()
> +{
> +  m_locations.release ();
> +}
> +
> +/* Construct a location for the given column
> +   within this line of a specific source file, if one doesn't exist
> +   already.  */
> +
> +deferred_location *
> +source_line::get_location (void *user_data, int column_num)
> +{
> +  int i;
> +  deferred_location *loc;
> +
> +  /* Another linear search that probably should be a hash table.  */
> +  FOR_EACH_VEC_ELT (m_locations, i, loc)
> +    if (loc->get_column_num () == column_num)
> +      return loc;
> +
> +  /* Not found.  */
> +  loc = new deferred_location (user_data, this, column_num);
> +  m_locations.safe_push (loc);
> +  return loc;
> +}
> +
> +/* Constructor for deferred_location.  */
> +deferred_location::deferred_location (void *user_data,
> +                                     source_line *line,
> +                                     int column_num) :
> +  m_srcloc (UNKNOWN_LOCATION),
> +  m_user_data (user_data),
> +  m_line (line),
> +  m_column_num(column_num)
> +{
> +}
> +
> +/* qsort comparator for comparing pairs of source_line *,
> +   ordering them by line number.  */
> +
> +static int
> +line_comparator (const void *lhs, const void *rhs)
> +{
> +  const source_line *line_lhs = *static_cast<const source_line * const*> (lhs);
> +  const source_line *line_rhs = *static_cast<const source_line * const*> (rhs);
> +  return line_lhs->get_line_num () - line_rhs->get_line_num ();
> +}
> +
> +/* qsort comparator for comparing pairs of deferred_location *,
> +   ordering them by column number.  */
> +
> +static int
> +location_comparator (const void *lhs, const void *rhs)
> +{
> +  const deferred_location *loc_lhs = \
> +    *static_cast<const deferred_location * const *> (lhs);
> +  const deferred_location *loc_rhs = \
> +    *static_cast<const deferred_location * const *> (rhs);
> +  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
> +}
> +
> +/* class deferred_locations */
> +
> +deferred_locations::deferred_locations ()
> +{
> +  m_source_files.create (0);
> +}
> +
> +/* Construct a source_file for the given source
> +   filename, if it doesn't exist already.  */
> +
> +source_file *
> +deferred_locations::get_source_file (const char *filename)
> +{
> +  /* Locate the file.
> +     For simplicitly, this is currently a linear search.
> +     Replace with a hash if this shows up in the profile.  */
> +  int i;
> +  source_file *file;
> +  tree ident_filename = get_identifier (filename);
> +
> +  FOR_EACH_VEC_ELT (m_source_files, i, file)
> +    if (file->filename_as_tree () == ident_filename)
> +      return file;
> +
> +  /* Not found.  */
> +  file = new source_file (ident_filename);
> +  m_source_files.safe_push (file);
> +  return file;
> +
> +}
> +
> +void
> +deferred_locations::add_to_line_table ()
> +{
> +  int i;
> +  source_file *file;
> +
> +  FOR_EACH_VEC_ELT (m_source_files, i, file)
> +    {
> +      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
> +
> +      /* Sort lines by ascending line numbers.  */
> +      file->m_source_lines.qsort (&line_comparator);
> +
> +      int j;
> +      source_line *line;
> +      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
> +       {
> +         int k;
> +         deferred_location *loc;
> +
> +         /* Sort locations in line by ascending column numbers.  */
> +         line->m_locations.qsort (&location_comparator);
> +
> +         /* Determine maximum column within this line.  */
> +         gcc_assert (line->m_locations.length () > 0);
> +         deferred_location *final_column =
> +           line->m_locations[line->m_locations.length () - 1];
> +         int max_col = final_column->get_column_num ();
> +
> +         linemap_line_start (line_table, line->get_line_num (), max_col);
> +         FOR_EACH_VEC_ELT (line->m_locations, k, loc)
> +           {
> +             loc->m_srcloc =
> +               linemap_position_for_column (line_table, loc->get_column_num ());
> +           }
> +       }
> +
> +      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> +    }
> +
> +}
> diff --git a/gcc/deferred-locations.h b/gcc/deferred-locations.h
> new file mode 100644
> index 0000000..209cb0d
> --- /dev/null
> +++ b/gcc/deferred-locations.h
> @@ -0,0 +1,139 @@
> +/* Dealing with the linemap API.
> +   Copyright (C) 2013-2016 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_DEFERRED_LOCATIONS_H
> +#define GCC_DEFERRED_LOCATIONS_H
> +
> +/* Dealing with the linemap API.
> +
> +   libcpp requires locations to be created as if by
> +   a tokenizer, creating them by filename, in ascending order of
> +   line/column, whereas the libgccjit API doesn't impose any such constraints:
> +   we allow client code to create locations in arbitrary orders.
> +
> +   To square this circle, we need to cache all location creation,
> +   grouping things up by filename/line, and then creating the linemap
> +   entries in a post-processing phase.  */
> +
> +/* A temporary wrapper object.
> +   These objects are (mostly) only valid during replay.
> +   We allocate them on the GC heap, so that they will be cleaned
> +   the next time the GC collects.  */
> +class wrapper
> +{
> +public:
> +  /* Allocate in the GC heap.  */
> +  void *operator new (size_t sz);
> +
> +  /* Some wrapper subclasses contain vec<> and so need to
> +     release them when they are GC-ed.  */
> +  virtual void finalizer () { }
> +};
> +
> +class source_file;
> +class source_line;
> +class deferred_location;
> +
> +/* A set of locations, all sharing a filename */
> +class source_file : public wrapper
> +{
> +public:
> +  source_file (tree filename);
> +  void finalizer ();
> +
> +  source_line *
> +  get_source_line (int line_num);
> +
> +  tree filename_as_tree () const { return m_filename; }
> +
> +  const char *get_filename () const;
> +
> +  vec<source_line *> m_source_lines;
> +
> +private:
> +  tree m_filename;
> +};
> +
> +/* A source line, with one or more locations of interest.  */
> +class source_line : public wrapper
> +{
> +public:
> +  source_line (source_file *file, int line_num);
> +  void finalizer ();
> +
> +  deferred_location *
> +  get_location (void *user_data, int column_num);
> +
> +  int get_line_num () const { return m_line_num; }
> +
> +  vec<deferred_location *> m_locations;
> +
> +private:
> +  source_file *m_source_file;
> +  int m_line_num;
> +};
> +
> +/* A specific location on a source line.  This is what we expose
> +   to the JIT client API.  */
> +class deferred_location : public wrapper
> +{
> +public:
> +  deferred_location (void *user_data, source_line *line, int column_num);
> +
> +  int get_column_num () const { return m_column_num; }
> +
> +  void *get_user_data () const { return m_user_data; }
> +
> +  source_location m_srcloc;
> +
> +private:
> +  void *m_user_data;
> +  source_line *m_line;
> +  int m_column_num;
> +};
> +
> +/* A class for managing the deferred creation of location_t values.
> +
> +   Locations are created in two phases:
> +
> +   - first phase: creation of source_file instances (via
> +   deferred_locations::get_source_file), from which source_line
> +   instances can be created (via source_file::get_source_line), from
> +   which deferred_location instances can be created (via
> +   source_line::get_location).
> +
> +   - second phase: call deferred_location::add_to_line_table, which
> +   walks all of the above and populates line_table, filling in
> +   the various deferred_location instances with source_location
> +   (aka location_t) values.  */
> +
> +class deferred_locations
> +{
> + public:
> +  deferred_locations ();
> +
> +  source_file *get_source_file (const char *filename);
> +
> +  void add_to_line_table ();
> +
> + private:
> +  auto_vec<source_file *> m_source_files;
> +};
> +
> +#endif /* #ifndef GCC_DEFERRED_LOCATIONS_H */
> diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
> index 8a6cd74..0a10cdf 100644
> --- a/gcc/jit/jit-common.h
> +++ b/gcc/jit/jit-common.h
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tree.h"
>  #include "inchash.h"
>  #include "tree-iterator.h"
> +#include "deferred-locations.h"
>
>  #ifdef GCC_VERSION
>  #if GCC_VERSION >= 4001
> @@ -146,9 +147,7 @@ namespace playback {
>      class rvalue;
>        class lvalue;
>          class param;
> -    class source_file;
> -    class source_line;
> -    class location;
> +    typedef deferred_location location;
>      class case_;
>
>    /* End of playback types. */
> diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
> index 579230d..f17c55b 100644
> --- a/gcc/jit/jit-playback.c
> +++ b/gcc/jit/jit-playback.c
> @@ -85,7 +85,6 @@ playback::context::context (recording::context *ctxt)
>    JIT_LOG_SCOPE (get_logger ());
>    m_functions.create (0);
>    m_globals.create (0);
> -  m_source_files.create (0);
>    m_cached_locations.create (0);
>  }
>
> @@ -2705,32 +2704,6 @@ get_path_so_file () const
>    return m_tempdir->get_path_so_file ();
>  }
>
> -/* qsort comparator for comparing pairs of playback::source_line *,
> -   ordering them by line number.  */
> -
> -static int
> -line_comparator (const void *lhs, const void *rhs)
> -{
> -  const playback::source_line *line_lhs = \
> -    *static_cast<const playback::source_line * const*> (lhs);
> -  const playback::source_line *line_rhs = \
> -    *static_cast<const playback::source_line * const*> (rhs);
> -  return line_lhs->get_line_num () - line_rhs->get_line_num ();
> -}
> -
> -/* qsort comparator for comparing pairs of playback::location *,
> -   ordering them by column number.  */
> -
> -static int
> -location_comparator (const void *lhs, const void *rhs)
> -{
> -  const playback::location *loc_lhs = \
> -    *static_cast<const playback::location * const *> (lhs);
> -  const playback::location *loc_rhs = \
> -    *static_cast<const playback::location * const *> (rhs);
> -  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
> -}
> -
>  /* Our API allows locations to be created in arbitrary orders, but the
>     linemap API requires locations to be created in ascending order
>     as if we were tokenizing files.
> @@ -2748,47 +2721,13 @@ handle_locations ()
>
>       line_table is a global.  */
>    JIT_LOG_SCOPE (get_logger ());
> -  int i;
> -  source_file *file;
> -
> -  FOR_EACH_VEC_ELT (m_source_files, i, file)
> -    {
> -      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
> -
> -      /* Sort lines by ascending line numbers.  */
> -      file->m_source_lines.qsort (&line_comparator);
> -
> -      int j;
> -      source_line *line;
> -      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
> -       {
> -         int k;
> -         location *loc;
> -
> -         /* Sort locations in line by ascending column numbers.  */
> -         line->m_locations.qsort (&location_comparator);
> -
> -         /* Determine maximum column within this line.  */
> -         gcc_assert (line->m_locations.length () > 0);
> -         location *final_column =
> -           line->m_locations[line->m_locations.length () - 1];
> -         int max_col = final_column->get_column_num ();
> -
> -         linemap_line_start (line_table, line->get_line_num (), max_col);
> -         FOR_EACH_VEC_ELT (line->m_locations, k, loc)
> -           {
> -             loc->m_srcloc =                                      \
> -               linemap_position_for_column (line_table, loc->get_column_num ());
> -           }
> -       }
> -
> -      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> -    }
>
> +  m_deferred_locations.add_to_line_table ();
>    /* line_table should now be populated; every playback::location should
>       now have an m_srcloc.  */
>
>    /* Now assign them to tree nodes as appropriate.  */
> +  int i;
>    std::pair<tree, location *> *cached_location;
>
>    FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location)
> @@ -2808,6 +2747,17 @@ handle_locations ()
>      }
>  }
>
> +/* Return the recording::location for playback::location LOC.
> +   Return NULL if LOC is NULL.  */
> +
> +static recording::location *
> +get_recording_loc (playback::location *loc)
> +{
> +  if (!loc)
> +    return NULL;
> +  return reinterpret_cast <recording::location *> (loc->get_user_data ());
> +}
> +
>  /* We handle errors on a playback::context by adding them to the
>     corresponding recording::context.  */
>
> @@ -2817,8 +2767,7 @@ add_error (location *loc, const char *fmt, ...)
>  {
>    va_list ap;
>    va_start (ap, fmt);
> -  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
> -                                 fmt, ap);
> +  m_recording_ctxt->add_error_va (get_recording_loc (loc), fmt, ap);
>    va_end (ap);
>  }
>
> @@ -2829,8 +2778,7 @@ void
>  playback::context::
>  add_error_va (location *loc, const char *fmt, va_list ap)
>  {
> -  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
> -                                 fmt, ap);
> +  m_recording_ctxt->add_error_va (get_recording_loc (loc), fmt, ap);
>  }
>
>  /* Dealing with the linemap API.  */
> @@ -2868,120 +2816,14 @@ set_tree_location (tree t, location *loc)
>  }
>
>
> -/* Construct a playback::source_file for the given source
> +/* Construct a source_file for the given source
>     filename, if it doesn't exist already.  */
>
> -playback::source_file *
> +source_file *
>  playback::context::
>  get_source_file (const char *filename)
>  {
> -  /* Locate the file.
> -     For simplicitly, this is currently a linear search.
> -     Replace with a hash if this shows up in the profile.  */
> -  int i;
> -  source_file *file;
> -  tree ident_filename = get_identifier (filename);
> -
> -  FOR_EACH_VEC_ELT (m_source_files, i, file)
> -    if (file->filename_as_tree () == ident_filename)
> -      return file;
> -
> -  /* Not found.  */
> -  file = new source_file (ident_filename);
> -  m_source_files.safe_push (file);
> -  return file;
> -}
> -
> -/* Constructor for gcc::jit::playback::source_file.  */
> -
> -playback::source_file::source_file (tree filename) :
> -  m_source_lines (),
> -  m_filename (filename)
> -{
> -}
> -
> -/* Don't leak vec's internal buffer (in non-GC heap) when we are
> -   GC-ed.  */
> -
> -void
> -playback::source_file::finalizer ()
> -{
> -  m_source_lines.release ();
> -}
> -
> -/* Construct a playback::source_line for the given line
> -   within this source file, if one doesn't exist already.  */
> -
> -playback::source_line *
> -playback::source_file::
> -get_source_line (int line_num)
> -{
> -  /* Locate the line.
> -     For simplicitly, this is currently a linear search.
> -     Replace with a hash if this shows up in the profile.  */
> -  int i;
> -  source_line *line;
> -
> -  FOR_EACH_VEC_ELT (m_source_lines, i, line)
> -    if (line->get_line_num () == line_num)
> -      return line;
> -
> -  /* Not found.  */
> -  line = new source_line (this, line_num);
> -  m_source_lines.safe_push (line);
> -  return line;
> -}
> -
> -/* Constructor for gcc::jit::playback::source_line.  */
> -
> -playback::source_line::source_line (source_file *file, int line_num) :
> -  m_locations (),
> -  m_source_file (file),
> -  m_line_num (line_num)
> -{
> -}
> -
> -/* Don't leak vec's internal buffer (in non-GC heap) when we are
> -   GC-ed.  */
> -
> -void
> -playback::source_line::finalizer ()
> -{
> -  m_locations.release ();
> -}
> -
> -/* Construct a playback::location for the given column
> -   within this line of a specific source file, if one doesn't exist
> -   already.  */
> -
> -playback::location *
> -playback::source_line::
> -get_location (recording::location *rloc, int column_num)
> -{
> -  int i;
> -  location *loc;
> -
> -  /* Another linear search that probably should be a hash table.  */
> -  FOR_EACH_VEC_ELT (m_locations, i, loc)
> -    if (loc->get_column_num () == column_num)
> -      return loc;
> -
> -  /* Not found.  */
> -  loc = new location (rloc, this, column_num);
> -  m_locations.safe_push (loc);
> -  return loc;
> -}
> -
> -/* Constructor for gcc::jit::playback::location.  */
> -
> -playback::location::location (recording::location *loc,
> -                             source_line *line,
> -                             int column_num) :
> -  m_srcloc (UNKNOWN_LOCATION),
> -  m_recording_loc (loc),
> -  m_line (line),
> -  m_column_num(column_num)
> -{
> +  return m_deferred_locations.get_source_file (filename);
>  }
>
>  /* The active gcc::jit::playback::context instance.  This is a singleton,
> diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
> index 7519bc4..86118d9 100644
> --- a/gcc/jit/jit-playback.h
> +++ b/gcc/jit/jit-playback.h
> @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include <utility> // for std::pair
>
>  #include "timevar.h"
> +#include "deferred-locations.h"
>
>  #include "jit-recording.h"
>
> @@ -305,7 +306,7 @@ private:
>    tree m_const_char_ptr;
>
>    /* Source location handling.  */
> -  auto_vec<source_file *> m_source_files;
> +  deferred_locations m_deferred_locations;
>
>    auto_vec<std::pair<tree, location *> > m_cached_locations;
>  };
> @@ -603,76 +604,6 @@ public:
>    {}
>  };
>
> -/* Dealing with the linemap API.
> -
> -   It appears that libcpp requires locations to be created as if by
> -   a tokenizer, creating them by filename, in ascending order of
> -   line/column, whereas our API doesn't impose any such constraints:
> -   we allow client code to create locations in arbitrary orders.
> -
> -   To square this circle, we need to cache all location creation,
> -   grouping things up by filename/line, and then creating the linemap
> -   entries in a post-processing phase.  */
> -
> -/* A set of locations, all sharing a filename */
> -class source_file : public wrapper
> -{
> -public:
> -  source_file (tree filename);
> -  void finalizer ();
> -
> -  source_line *
> -  get_source_line (int line_num);
> -
> -  tree filename_as_tree () const { return m_filename; }
> -
> -  const char*
> -  get_filename () const { return IDENTIFIER_POINTER (m_filename); }
> -
> -  vec<source_line *> m_source_lines;
> -
> -private:
> -  tree m_filename;
> -};
> -
> -/* A source line, with one or more locations of interest.  */
> -class source_line : public wrapper
> -{
> -public:
> -  source_line (source_file *file, int line_num);
> -  void finalizer ();
> -
> -  location *
> -  get_location (recording::location *rloc, int column_num);
> -
> -  int get_line_num () const { return m_line_num; }
> -
> -  vec<location *> m_locations;
> -
> -private:
> -  source_file *m_source_file;
> -  int m_line_num;
> -};
> -
> -/* A specific location on a source line.  This is what we expose
> -   to the client API.  */
> -class location : public wrapper
> -{
> -public:
> -  location (recording::location *loc, source_line *line, int column_num);
> -
> -  int get_column_num () const { return m_column_num; }
> -
> -  recording::location *get_recording_loc () const { return m_recording_loc; }
> -
> -  source_location m_srcloc;
> -
> -private:
> -  recording::location *m_recording_loc;
> -  source_line *m_line;
> -  int m_column_num;
> -};
> -
>  } // namespace gcc::jit::playback
>
>  extern playback::context *active_playback_ctxt;
> --
> 1.8.5.3
>
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6c5adc0..c61f303 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1239,6 +1239,7 @@  OBJS = \
 	dce.o \
 	ddg.o \
 	debug.o \
+	deferred-locations.o \
 	df-core.o \
 	df-problems.o \
 	df-scan.o \
diff --git a/gcc/deferred-locations.c b/gcc/deferred-locations.c
new file mode 100644
index 0000000..cd02938
--- /dev/null
+++ b/gcc/deferred-locations.c
@@ -0,0 +1,240 @@ 
+/* Dealing with the linemap API.
+   Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "input.h"
+#include "deferred-locations.h"
+#include "stringpool.h"
+#include "tree.h"
+
+/* The wrapper subclasses are GC-managed, but can own non-GC memory.
+   Provide this finalization hook for calling then they are collected,
+   which calls the finalizer vfunc.  This allows them to call "release"
+   on any vec<> within them.  */
+
+static void
+wrapper_finalizer (void *ptr)
+{
+  wrapper *w = reinterpret_cast <wrapper *> (ptr);
+  w->finalizer ();
+}
+
+/* wrapper subclasses are GC-managed:
+   allocate them using ggc_internal_cleared_alloc.  */
+
+void *
+wrapper::operator new (size_t sz)
+{
+  return ggc_internal_cleared_alloc (sz, wrapper_finalizer, 0, 1);
+
+}
+
+/* Constructor for source_file.  */
+
+source_file::source_file (tree filename) :
+  m_source_lines (),
+  m_filename (filename)
+{
+}
+
+/* Don't leak vec's internal buffer (in non-GC heap) when we are
+   GC-ed.  */
+
+void
+source_file::finalizer ()
+{
+  m_source_lines.release ();
+}
+
+/* Construct a source_line for the given line
+   within this source file, if one doesn't exist already.  */
+
+source_line *
+source_file::get_source_line (int line_num)
+{
+  /* Locate the line.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_line *line;
+
+  FOR_EACH_VEC_ELT (m_source_lines, i, line)
+    if (line->get_line_num () == line_num)
+      return line;
+
+  /* Not found.  */
+  line = new source_line (this, line_num);
+  m_source_lines.safe_push (line);
+  return line;
+}
+
+const char *
+source_file::get_filename () const
+{
+  return IDENTIFIER_POINTER (m_filename);
+}
+
+/* Constructor for source_line.  */
+
+source_line::source_line (source_file *file, int line_num) :
+  m_locations (),
+  m_source_file (file),
+  m_line_num (line_num)
+{
+}
+
+/* Don't leak vec's internal buffer (in non-GC heap) when we are
+   GC-ed.  */
+
+void
+source_line::finalizer ()
+{
+  m_locations.release ();
+}
+
+/* Construct a location for the given column
+   within this line of a specific source file, if one doesn't exist
+   already.  */
+
+deferred_location *
+source_line::get_location (void *user_data, int column_num)
+{
+  int i;
+  deferred_location *loc;
+
+  /* Another linear search that probably should be a hash table.  */
+  FOR_EACH_VEC_ELT (m_locations, i, loc)
+    if (loc->get_column_num () == column_num)
+      return loc;
+
+  /* Not found.  */
+  loc = new deferred_location (user_data, this, column_num);
+  m_locations.safe_push (loc);
+  return loc;
+}
+
+/* Constructor for deferred_location.  */
+deferred_location::deferred_location (void *user_data,
+				      source_line *line,
+				      int column_num) :
+  m_srcloc (UNKNOWN_LOCATION),
+  m_user_data (user_data),
+  m_line (line),
+  m_column_num(column_num)
+{
+}
+
+/* qsort comparator for comparing pairs of source_line *,
+   ordering them by line number.  */
+
+static int
+line_comparator (const void *lhs, const void *rhs)
+{
+  const source_line *line_lhs = *static_cast<const source_line * const*> (lhs);
+  const source_line *line_rhs = *static_cast<const source_line * const*> (rhs);
+  return line_lhs->get_line_num () - line_rhs->get_line_num ();
+}
+
+/* qsort comparator for comparing pairs of deferred_location *,
+   ordering them by column number.  */
+
+static int
+location_comparator (const void *lhs, const void *rhs)
+{
+  const deferred_location *loc_lhs = \
+    *static_cast<const deferred_location * const *> (lhs);
+  const deferred_location *loc_rhs = \
+    *static_cast<const deferred_location * const *> (rhs);
+  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
+}
+
+/* class deferred_locations */
+
+deferred_locations::deferred_locations ()
+{
+  m_source_files.create (0);
+}
+
+/* Construct a source_file for the given source
+   filename, if it doesn't exist already.  */
+
+source_file *
+deferred_locations::get_source_file (const char *filename)
+{
+  /* Locate the file.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_file *file;
+  tree ident_filename = get_identifier (filename);
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    if (file->filename_as_tree () == ident_filename)
+      return file;
+
+  /* Not found.  */
+  file = new source_file (ident_filename);
+  m_source_files.safe_push (file);
+  return file;
+
+}
+
+void
+deferred_locations::add_to_line_table ()
+{
+  int i;
+  source_file *file;
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    {
+      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
+
+      /* Sort lines by ascending line numbers.  */
+      file->m_source_lines.qsort (&line_comparator);
+
+      int j;
+      source_line *line;
+      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
+	{
+	  int k;
+	  deferred_location *loc;
+
+	  /* Sort locations in line by ascending column numbers.  */
+	  line->m_locations.qsort (&location_comparator);
+
+	  /* Determine maximum column within this line.  */
+	  gcc_assert (line->m_locations.length () > 0);
+	  deferred_location *final_column =
+	    line->m_locations[line->m_locations.length () - 1];
+	  int max_col = final_column->get_column_num ();
+
+	  linemap_line_start (line_table, line->get_line_num (), max_col);
+	  FOR_EACH_VEC_ELT (line->m_locations, k, loc)
+	    {
+	      loc->m_srcloc =
+		linemap_position_for_column (line_table, loc->get_column_num ());
+	    }
+	}
+
+      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+    }
+
+}
diff --git a/gcc/deferred-locations.h b/gcc/deferred-locations.h
new file mode 100644
index 0000000..209cb0d
--- /dev/null
+++ b/gcc/deferred-locations.h
@@ -0,0 +1,139 @@ 
+/* Dealing with the linemap API.
+   Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_DEFERRED_LOCATIONS_H
+#define GCC_DEFERRED_LOCATIONS_H
+
+/* Dealing with the linemap API.
+
+   libcpp requires locations to be created as if by
+   a tokenizer, creating them by filename, in ascending order of
+   line/column, whereas the libgccjit API doesn't impose any such constraints:
+   we allow client code to create locations in arbitrary orders.
+
+   To square this circle, we need to cache all location creation,
+   grouping things up by filename/line, and then creating the linemap
+   entries in a post-processing phase.  */
+
+/* A temporary wrapper object.
+   These objects are (mostly) only valid during replay.
+   We allocate them on the GC heap, so that they will be cleaned
+   the next time the GC collects.  */
+class wrapper
+{
+public:
+  /* Allocate in the GC heap.  */
+  void *operator new (size_t sz);
+
+  /* Some wrapper subclasses contain vec<> and so need to
+     release them when they are GC-ed.  */
+  virtual void finalizer () { }
+};
+
+class source_file;
+class source_line;
+class deferred_location;
+
+/* A set of locations, all sharing a filename */
+class source_file : public wrapper
+{
+public:
+  source_file (tree filename);
+  void finalizer ();
+
+  source_line *
+  get_source_line (int line_num);
+
+  tree filename_as_tree () const { return m_filename; }
+
+  const char *get_filename () const;
+
+  vec<source_line *> m_source_lines;
+
+private:
+  tree m_filename;
+};
+
+/* A source line, with one or more locations of interest.  */
+class source_line : public wrapper
+{
+public:
+  source_line (source_file *file, int line_num);
+  void finalizer ();
+
+  deferred_location *
+  get_location (void *user_data, int column_num);
+
+  int get_line_num () const { return m_line_num; }
+
+  vec<deferred_location *> m_locations;
+
+private:
+  source_file *m_source_file;
+  int m_line_num;
+};
+
+/* A specific location on a source line.  This is what we expose
+   to the JIT client API.  */
+class deferred_location : public wrapper
+{
+public:
+  deferred_location (void *user_data, source_line *line, int column_num);
+
+  int get_column_num () const { return m_column_num; }
+
+  void *get_user_data () const { return m_user_data; }
+
+  source_location m_srcloc;
+
+private:
+  void *m_user_data;
+  source_line *m_line;
+  int m_column_num;
+};
+
+/* A class for managing the deferred creation of location_t values.
+
+   Locations are created in two phases:
+
+   - first phase: creation of source_file instances (via
+   deferred_locations::get_source_file), from which source_line
+   instances can be created (via source_file::get_source_line), from
+   which deferred_location instances can be created (via
+   source_line::get_location).
+
+   - second phase: call deferred_location::add_to_line_table, which
+   walks all of the above and populates line_table, filling in
+   the various deferred_location instances with source_location
+   (aka location_t) values.  */
+
+class deferred_locations
+{
+ public:
+  deferred_locations ();
+
+  source_file *get_source_file (const char *filename);
+
+  void add_to_line_table ();
+
+ private:
+  auto_vec<source_file *> m_source_files;
+};
+
+#endif /* #ifndef GCC_DEFERRED_LOCATIONS_H */
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 8a6cd74..0a10cdf 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -27,6 +27,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "inchash.h"
 #include "tree-iterator.h"
+#include "deferred-locations.h"
 
 #ifdef GCC_VERSION
 #if GCC_VERSION >= 4001
@@ -146,9 +147,7 @@  namespace playback {
     class rvalue;
       class lvalue;
         class param;
-    class source_file;
-    class source_line;
-    class location;
+    typedef deferred_location location;
     class case_;
 
   /* End of playback types. */
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 579230d..f17c55b 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -85,7 +85,6 @@  playback::context::context (recording::context *ctxt)
   JIT_LOG_SCOPE (get_logger ());
   m_functions.create (0);
   m_globals.create (0);
-  m_source_files.create (0);
   m_cached_locations.create (0);
 }
 
@@ -2705,32 +2704,6 @@  get_path_so_file () const
   return m_tempdir->get_path_so_file ();
 }
 
-/* qsort comparator for comparing pairs of playback::source_line *,
-   ordering them by line number.  */
-
-static int
-line_comparator (const void *lhs, const void *rhs)
-{
-  const playback::source_line *line_lhs = \
-    *static_cast<const playback::source_line * const*> (lhs);
-  const playback::source_line *line_rhs = \
-    *static_cast<const playback::source_line * const*> (rhs);
-  return line_lhs->get_line_num () - line_rhs->get_line_num ();
-}
-
-/* qsort comparator for comparing pairs of playback::location *,
-   ordering them by column number.  */
-
-static int
-location_comparator (const void *lhs, const void *rhs)
-{
-  const playback::location *loc_lhs = \
-    *static_cast<const playback::location * const *> (lhs);
-  const playback::location *loc_rhs = \
-    *static_cast<const playback::location * const *> (rhs);
-  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
-}
-
 /* Our API allows locations to be created in arbitrary orders, but the
    linemap API requires locations to be created in ascending order
    as if we were tokenizing files.
@@ -2748,47 +2721,13 @@  handle_locations ()
 
      line_table is a global.  */
   JIT_LOG_SCOPE (get_logger ());
-  int i;
-  source_file *file;
-
-  FOR_EACH_VEC_ELT (m_source_files, i, file)
-    {
-      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
-
-      /* Sort lines by ascending line numbers.  */
-      file->m_source_lines.qsort (&line_comparator);
-
-      int j;
-      source_line *line;
-      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
-	{
-	  int k;
-	  location *loc;
-
-	  /* Sort locations in line by ascending column numbers.  */
-	  line->m_locations.qsort (&location_comparator);
-
-	  /* Determine maximum column within this line.  */
-	  gcc_assert (line->m_locations.length () > 0);
-	  location *final_column =
-	    line->m_locations[line->m_locations.length () - 1];
-	  int max_col = final_column->get_column_num ();
-
-	  linemap_line_start (line_table, line->get_line_num (), max_col);
-	  FOR_EACH_VEC_ELT (line->m_locations, k, loc)
-	    {
-	      loc->m_srcloc =					   \
-		linemap_position_for_column (line_table, loc->get_column_num ());
-	    }
-	}
-
-      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
-    }
 
+  m_deferred_locations.add_to_line_table ();
   /* line_table should now be populated; every playback::location should
      now have an m_srcloc.  */
 
   /* Now assign them to tree nodes as appropriate.  */
+  int i;
   std::pair<tree, location *> *cached_location;
 
   FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location)
@@ -2808,6 +2747,17 @@  handle_locations ()
     }
 }
 
+/* Return the recording::location for playback::location LOC.
+   Return NULL if LOC is NULL.  */
+
+static recording::location *
+get_recording_loc (playback::location *loc)
+{
+  if (!loc)
+    return NULL;
+  return reinterpret_cast <recording::location *> (loc->get_user_data ());
+}
+
 /* We handle errors on a playback::context by adding them to the
    corresponding recording::context.  */
 
@@ -2817,8 +2767,7 @@  add_error (location *loc, const char *fmt, ...)
 {
   va_list ap;
   va_start (ap, fmt);
-  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
-				  fmt, ap);
+  m_recording_ctxt->add_error_va (get_recording_loc (loc), fmt, ap);
   va_end (ap);
 }
 
@@ -2829,8 +2778,7 @@  void
 playback::context::
 add_error_va (location *loc, const char *fmt, va_list ap)
 {
-  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
-				  fmt, ap);
+  m_recording_ctxt->add_error_va (get_recording_loc (loc), fmt, ap);
 }
 
 /* Dealing with the linemap API.  */
@@ -2868,120 +2816,14 @@  set_tree_location (tree t, location *loc)
 }
 
 
-/* Construct a playback::source_file for the given source
+/* Construct a source_file for the given source
    filename, if it doesn't exist already.  */
 
-playback::source_file *
+source_file *
 playback::context::
 get_source_file (const char *filename)
 {
-  /* Locate the file.
-     For simplicitly, this is currently a linear search.
-     Replace with a hash if this shows up in the profile.  */
-  int i;
-  source_file *file;
-  tree ident_filename = get_identifier (filename);
-
-  FOR_EACH_VEC_ELT (m_source_files, i, file)
-    if (file->filename_as_tree () == ident_filename)
-      return file;
-
-  /* Not found.  */
-  file = new source_file (ident_filename);
-  m_source_files.safe_push (file);
-  return file;
-}
-
-/* Constructor for gcc::jit::playback::source_file.  */
-
-playback::source_file::source_file (tree filename) :
-  m_source_lines (),
-  m_filename (filename)
-{
-}
-
-/* Don't leak vec's internal buffer (in non-GC heap) when we are
-   GC-ed.  */
-
-void
-playback::source_file::finalizer ()
-{
-  m_source_lines.release ();
-}
-
-/* Construct a playback::source_line for the given line
-   within this source file, if one doesn't exist already.  */
-
-playback::source_line *
-playback::source_file::
-get_source_line (int line_num)
-{
-  /* Locate the line.
-     For simplicitly, this is currently a linear search.
-     Replace with a hash if this shows up in the profile.  */
-  int i;
-  source_line *line;
-
-  FOR_EACH_VEC_ELT (m_source_lines, i, line)
-    if (line->get_line_num () == line_num)
-      return line;
-
-  /* Not found.  */
-  line = new source_line (this, line_num);
-  m_source_lines.safe_push (line);
-  return line;
-}
-
-/* Constructor for gcc::jit::playback::source_line.  */
-
-playback::source_line::source_line (source_file *file, int line_num) :
-  m_locations (),
-  m_source_file (file),
-  m_line_num (line_num)
-{
-}
-
-/* Don't leak vec's internal buffer (in non-GC heap) when we are
-   GC-ed.  */
-
-void
-playback::source_line::finalizer ()
-{
-  m_locations.release ();
-}
-
-/* Construct a playback::location for the given column
-   within this line of a specific source file, if one doesn't exist
-   already.  */
-
-playback::location *
-playback::source_line::
-get_location (recording::location *rloc, int column_num)
-{
-  int i;
-  location *loc;
-
-  /* Another linear search that probably should be a hash table.  */
-  FOR_EACH_VEC_ELT (m_locations, i, loc)
-    if (loc->get_column_num () == column_num)
-      return loc;
-
-  /* Not found.  */
-  loc = new location (rloc, this, column_num);
-  m_locations.safe_push (loc);
-  return loc;
-}
-
-/* Constructor for gcc::jit::playback::location.  */
-
-playback::location::location (recording::location *loc,
-			      source_line *line,
-			      int column_num) :
-  m_srcloc (UNKNOWN_LOCATION),
-  m_recording_loc (loc),
-  m_line (line),
-  m_column_num(column_num)
-{
+  return m_deferred_locations.get_source_file (filename);
 }
 
 /* The active gcc::jit::playback::context instance.  This is a singleton,
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 7519bc4..86118d9 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -24,6 +24,7 @@  along with GCC; see the file COPYING3.  If not see
 #include <utility> // for std::pair
 
 #include "timevar.h"
+#include "deferred-locations.h"
 
 #include "jit-recording.h"
 
@@ -305,7 +306,7 @@  private:
   tree m_const_char_ptr;
 
   /* Source location handling.  */
-  auto_vec<source_file *> m_source_files;
+  deferred_locations m_deferred_locations;
 
   auto_vec<std::pair<tree, location *> > m_cached_locations;
 };
@@ -603,76 +604,6 @@  public:
   {}
 };
 
-/* Dealing with the linemap API.
-
-   It appears that libcpp requires locations to be created as if by
-   a tokenizer, creating them by filename, in ascending order of
-   line/column, whereas our API doesn't impose any such constraints:
-   we allow client code to create locations in arbitrary orders.
-
-   To square this circle, we need to cache all location creation,
-   grouping things up by filename/line, and then creating the linemap
-   entries in a post-processing phase.  */
-
-/* A set of locations, all sharing a filename */
-class source_file : public wrapper
-{
-public:
-  source_file (tree filename);
-  void finalizer ();
-
-  source_line *
-  get_source_line (int line_num);
-
-  tree filename_as_tree () const { return m_filename; }
-
-  const char*
-  get_filename () const { return IDENTIFIER_POINTER (m_filename); }
-
-  vec<source_line *> m_source_lines;
-
-private:
-  tree m_filename;
-};
-
-/* A source line, with one or more locations of interest.  */
-class source_line : public wrapper
-{
-public:
-  source_line (source_file *file, int line_num);
-  void finalizer ();
-
-  location *
-  get_location (recording::location *rloc, int column_num);
-
-  int get_line_num () const { return m_line_num; }
-
-  vec<location *> m_locations;
-
-private:
-  source_file *m_source_file;
-  int m_line_num;
-};
-
-/* A specific location on a source line.  This is what we expose
-   to the client API.  */
-class location : public wrapper
-{
-public:
-  location (recording::location *loc, source_line *line, int column_num);
-
-  int get_column_num () const { return m_column_num; }
-
-  recording::location *get_recording_loc () const { return m_recording_loc; }
-
-  source_location m_srcloc;
-
-private:
-  recording::location *m_recording_loc;
-  source_line *m_line;
-  int m_column_num;
-};
-
 } // namespace gcc::jit::playback
 
 extern playback::context *active_playback_ctxt;