diff mbox

[committed] jit: New API entrypoint: gcc_jit_context_dump_reproducer_to_file

Message ID 1421187944-40099-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Jan. 13, 2015, 10:25 p.m. UTC
When debugging libgccjit or client code, the recipe for reproducing a bug
can be very awkward.  For example, consider client code, linked against
many libraries, which parses some source file into some internal
representation, and then walks this IR, calling into libgccjit.  If this
encounters a bug (e.g. an ICE deep inside gcc) then reproducing it would
require the client code and its dependencies (e.g. data).

This patch adds an API entrypoint:
  gcc_jit_context_dump_reproducer_to_file
which will write out C code for a simple executable linked only to
libgccjit that performs the equivalent calls into libgccjit, without
needing the client code and its data.

This may be useful when debugging the library or client code, for
reducing a complicated recipe for reproducing a bug into a simpler form.

To give us test coverage, the patch also updates the testsuite, so that
most testcase now call this entrypoint, and jit.exp then verifies that
.c reproducers were indeed written, and verifies that they compile.

It doesn't yet verify that they run.  Doing so would be almost a quine;
in theory we could verify that each reproducer can output its own source
code, diffing the results. Implementing that is left for another day
(or as an exercise to the reader...).

This patch takes jit.sum from 7362 to 7494 passes.

Committed to trunk as r219564.

gcc/jit/ChangeLog:
	* docs/cp/topics/contexts.rst (Debugging): Add
	gccjit::context::dump_reproducer_to_file.
	* docs/internals/index.rst (Design notes): New section,
	discussing input validation and
	gcc_jit_context_dump_reproducer_to_file.
	* docs/topics/contexts.rst (Debugging): Add
	gcc_jit_context_dump_reproducer_to_file.
	* docs/_build/texinfo/libgccjit.texi: Regenerate.
	* jit-common.h (gcc::jit::dump::get_context): New accessor.
	* jit-recording.c: Include "hash-map.h".
	Within namespace ::gcc::jit...
	(dump::write): Flush each line.
	(dump::make_location): Pass false for new param "created_by_user".
	(class allocator): New class.
	(allocator::~allocator): New function.
	(allocator::xstrdup_printf): New function.
	(allocator::xstrdup_printf_va): New function.
	(class reproducer): New subclass of dump.
	(reproducer::reproducer): New function.
	(reproducer::write_params): New function.
	(reproducer::write_args): New function.
	(reproducer::make_identifier): New function.
	(reproducer::make_tmp_identifier): New function.
	(reproducer::get_identifier): New pair of functions.
	(reproducer::get_identifier_as_rvalue): New function.
	(reproducer::get_identifier_as_lvalue): New function.
	(reproducer::get_identifier_as_type): New function.
	(reproducer::xstrdup_printf): New function.
	(recording::context::context): Initialize m_toplevel_ctxt.
	(recording::context::new_location): Add param created_by_user.
	(str_option_reproducer_strings): New table of strings.
	(int_option_reproducer_strings): Likewise.
	(bool_option_reproducer_strings): Likewise.
	(get_type_enum_strings): Likewise.
	(names_of_function_kinds): Likewise.
	(global_kind_reproducer_strings): Likewise.
	(unary_op_reproducer_strings): Likewise.
	(binary_op_reproducer_strings): Likewise.
	(comparison_reproducer_strings): Likewise.
	Within namespace ::gcc::jit::recording::...
	(context::dump_reproducer_to_file): New function.
	(string::write_reproducer): Likewise.
	(location::write_reproducer): Likewise.
	(type::access_as_type): Likewise.
	(memento_of_get_type::write_reproducer): Likewise.
	(memento_of_get_pointer::write_reproducer): Likewise.
	(memento_of_get_const::write_reproducer): Likewise.
	(memento_of_get_volatile::write_reproducer): Likewise.
	(array_type::write_reproducer): Likewise.
	(function_type::write_reproducer): Likewise.
	(function_type::write_deferred_reproducer): Likewise.
	(field::write_reproducer): Likewise.
	(struct_::access_as_type): Likewise.
	(struct_::write_reproducer): Likewise.
	(union_::write_reproducer): Likewise.
	(fields::write_reproducer): Likewise.
	(rvalue::access_as_rvalue): Likewise.
	(lvalue::access_as_rvalue): Likewise.
	(lvalue::access_as_lvalue): Likewise.
	(param::access_as_rvalue): Likewise.
	(param::access_as_lvalue): Likewise.
	(param::write_reproducer): Likewise.
	(function::write_reproducer): Likewise.
	(block::write_reproducer): Likewise.
	(global::write_reproducer): Likewise.
	(memento_of_new_rvalue_from_const <int>::write_reproducer):
	Likewise.
	(memento_of_new_rvalue_from_const <long>::write_reproducer):
	Likewise.
	(memento_of_new_rvalue_from_const <double>::write_reproducer):
	Likewise.
	(memento_of_new_rvalue_from_const <void *>::write_reproducer):
	Likewise.
	(memento_of_new_string_literal::write_reproducer): Likewise.
	(unary_op::write_reproducer): Likewise.
	(binary_op::write_reproducer): Likewise.
	(comparison::write_reproducer): Likewise.
	(cast::write_reproducer): Likewise.
	(call::write_reproducer): Likewise.
	(call_through_ptr::write_reproducer): Likewise.
	(array_access::write_reproducer): Likewise.
	(access_field_of_lvalue::write_reproducer): Likewise.
	(access_field_rvalue::write_reproducer): Likewise.
	(dereference_field_rvalue::write_reproducer): Likewise.
	(dereference_rvalue::write_reproducer): Likewise.
	(get_address_of_lvalue::write_reproducer): Likewise.
	(local::write_reproducer): Likewise.
	(eval::write_reproducer): Likewise.
	(assignment::write_reproducer): Likewise.
	(assignment_op::write_reproducer): Likewise.
	(comment::write_reproducer): Likewise.
	(conditional::write_reproducer): Likewise.
	(jump::write_reproducer): Likewise.
	(return_::write_reproducer): Likewise.
	* jit-recording.h (gcc::jit::reproducer): New forward declararion.
	Within namespace ::gcc::jit::recording::...
	(context::new_location): Add "created_by_user" param.
	(context::dump_reproducer_to_file): New method.
	(context::m_toplevel_ctxt): New field.
	(memento::write_reproducer): New pure virtual function.
	(memento::dyn_cast_location): New virtual function.
	(string::write_reproducer):
	(location::location): Add "created_by_user" param.
	(location::dyn_cast_location): New function.
	(location::created_by_user): New accessor.
	(location::write_reproducer): New function.
	(location::m_created_by_user): New field.
	(type::access_as_type): New virtual function.
	(location::write_reproducer): Likewise.
	(type::access_as_type): Likewise.
	(memento_of_get_type::write_reproducer): Likewise.
	(memento_of_get_pointer::write_reproducer): Likewise.
	(memento_of_get_const::write_reproducer): Likewise.
	(memento_of_get_volatile::write_reproducer): Likewise.
	(array_type::write_reproducer): Likewise.
	(function_type::write_reproducer): Likewise.
	(function_type::write_deferred_reproducer): Likewise.
	(field::write_reproducer): Likewise.
	(struct_::access_as_type): Likewise.
	(struct_::write_reproducer): Likewise.
	(union_::write_reproducer): Likewise.
	(union_::m_fields): Remove stray unused field.
	(fields::length): New accessor.
	(fields::get_field): New accessor.
	(fields::write_reproducer): New function.
	(rvalue::access_as_rvalue): Likewise.
	(lvalue::access_as_rvalue): Likewise.
	(lvalue::access_as_lvalue): Likewise.
	(param::access_as_rvalue): Likewise.
	(param::access_as_lvalue): Likewise.
	(param::write_reproducer): Likewise.
	(function::write_reproducer): Likewise.
	(block::write_reproducer): Likewise.
	(global::write_reproducer): Likewise.
	(memento_of_new_rvalue_from_const <HOST_TYPE>::write_reproducer):
	Likewise.
	(memento_of_new_string_literal::write_reproducer): Likewise.
	(unary_op::write_reproducer): Likewise.
	(binary_op::write_reproducer): Likewise.
	(comparison::write_reproducer): Likewise.
	(cast::write_reproducer): Likewise.
	(call::write_reproducer): Likewise.
	(call_through_ptr::write_reproducer): Likewise.
	(array_access::write_reproducer): Likewise.
	(access_field_of_lvalue::write_reproducer): Likewise.
	(access_field_rvalue::write_reproducer): Likewise.
	(dereference_field_rvalue::write_reproducer): Likewise.
	(dereference_rvalue::write_reproducer): Likewise.
	(get_address_of_lvalue::write_reproducer): Likewise.
	(local::write_reproducer): Likewise.
	(eval::write_reproducer): Likewise.
	(assignment::write_reproducer): Likewise.
	(assignment_op::write_reproducer): Likewise.
	(comment::write_reproducer): Likewise.
	(conditional::write_reproducer): Likewise.
	(jump::write_reproducer): Likewise.
	(return_::write_reproducer): Likewise.
	* libgccjit++.h (gccjit::context::dump_reproducer_to_file): New.
	* libgccjit.c (gcc_jit_context_new_location): Pass "true" as
	param "created_by_user".
	(gcc_jit_context_dump_reproducer_to_file): New API entrypoint.
	* libgccjit.h (gcc_jit_context_dump_reproducer_to_file): New API
	entrypoint.
	* libgccjit.map (gcc_jit_context_dump_reproducer_to_file): New API
	entrypoint.

gcc/testsuite/ChangeLog:
	* jit.dg/harness.h (set_up_logging): Move string concatenation
	into...
	(concat_strings): New function.
	(dump_reproducer): New function.
	(test_jit): Call dump_reproducer.
	* jit.dg/jit.exp (is_testcase_meant_to_generate_a_reproducer): New
	function.
	(jit-dg-test): Delete any generated reproducer from previous runs.
	Verify that a generated reproducer was created, and verify that it
	compiles.
	* jit.dg/test-nested-contexts.c (main): Call
	gcc_jit_context_dump_reproducer_to_file.
---
 gcc/jit/docs/cp/topics/contexts.rst         |   10 +
 gcc/jit/docs/internals/index.rst            |   10 +
 gcc/jit/docs/topics/contexts.rst            |   24 +
 gcc/jit/jit-common.h                        |    2 +
 gcc/jit/jit-recording.c                     | 1462 ++++++++++++++++++++++++++-
 gcc/jit/jit-recording.h                     |   81 +-
 gcc/jit/libgccjit++.h                       |    9 +
 gcc/jit/libgccjit.c                         |   18 +-
 gcc/jit/libgccjit.h                         |   18 +
 gcc/jit/libgccjit.map                       |    1 +
 gcc/testsuite/jit.dg/harness.h              |   47 +-
 gcc/testsuite/jit.dg/jit.exp                |   71 ++
 gcc/testsuite/jit.dg/test-nested-contexts.c |    8 +
 13 files changed, 1734 insertions(+), 27 deletions(-)
diff mbox

Patch

diff --git a/gcc/jit/docs/cp/topics/contexts.rst b/gcc/jit/docs/cp/topics/contexts.rst
index 4becd51..eb54e77 100644
--- a/gcc/jit/docs/cp/topics/contexts.rst
+++ b/gcc/jit/docs/cp/topics/contexts.rst
@@ -144,6 +144,16 @@  Debugging
    :c:macro:`GCCJIT::BOOL_OPTION_DEBUGINFO` to allow stepping through the
    code in a debugger.
 
+.. function:: void\
+              gccjit::context::dump_reproducer_to_file (gcc_jit_context *ctxt,\
+                                                        const char *path)
+
+   This is a thin wrapper around the C API
+   :c:func:`gcc_jit_context_dump_reproducer_to_file`, and hence works the
+   same way.
+
+   Note that the generated source is C code, not C++; this might be of use
+   for seeing what the C++ bindings are doing at the C level.
 
 Options
 -------
diff --git a/gcc/jit/docs/internals/index.rst b/gcc/jit/docs/internals/index.rst
index 20ac337..0d8d5f6 100644
--- a/gcc/jit/docs/internals/index.rst
+++ b/gcc/jit/docs/internals/index.rst
@@ -277,3 +277,13 @@  generated via this call:
 
 .. literalinclude:: test-hello-world.exe.log.txt
     :lines: 1-
+
+Design notes
+------------
+It should not be possible for client code to cause an internal compiler
+error.  If this *does* happen, the root cause should be isolated (perhaps
+using :c:func:`gcc_jit_context_dump_reproducer_to_file`) and the cause
+should be rejected via additional checking.  The checking ideally should
+be within the libgccjit API entrypoints in libgccjit.c, since this is as
+close as possible to the error; failing that, a good place is within
+``recording::context::validate ()`` in jit-recording.c.
diff --git a/gcc/jit/docs/topics/contexts.rst b/gcc/jit/docs/topics/contexts.rst
index 3dc313c..d62217b 100644
--- a/gcc/jit/docs/topics/contexts.rst
+++ b/gcc/jit/docs/topics/contexts.rst
@@ -218,6 +218,30 @@  current state of a context to the given path, whereas
 :c:func:`gcc_jit_context_set_logfile` enables on-going logging of
 future activies on a context to the given `FILE *`.
 
+
+.. function:: void\
+              gcc_jit_context_dump_reproducer_to_file (gcc_jit_context *ctxt,\
+                                                       const char *path)
+
+   Write C source code into `path` that can be compiled into a
+   self-contained executable (i.e. with libgccjit as the only dependency).
+   The generated code will attempt to replay the API calls that have been
+   made into the given context.
+
+   This may be useful when debugging the library or client code, for
+   reducing a complicated recipe for reproducing a bug into a simpler
+   form.  For example, consider client code that parses some source file
+   into some internal representation, and then walks this IR, calling into
+   libgccjit.  If this encounters a bug, a call to
+   `gcc_jit_context_dump_reproducer_to_file` will write out C code for
+   a much simpler executable that performs the equivalent calls into
+   libgccjit, without needing the client code and its data.
+
+   Typically you need to supply :option:`-Wno-unused-variable` when
+   compiling the generated file (since the result of each API call is
+   assigned to a unique variable within the generated C source, and not
+   all are necessarily then used).
+
 .. function:: void\
               gcc_jit_context_enable_dump (gcc_jit_context *ctxt,\
                                            const char *dumpname, \
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 78dc1c5..09d63ba 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -168,6 +168,8 @@  public:
 	bool update_locations);
   ~dump ();
 
+  recording::context &get_context () { return m_ctxt; }
+
   void write (const char *fmt, ...)
     GNU_PRINTF(2, 3);
 
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index ec247e5..76eabbd 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "pretty-print.h"
+#include "hash-map.h"
 
 #include <pthread.h>
 
@@ -98,6 +99,9 @@  dump::write (const char *fmt, ...)
     m_ctxt.add_error (NULL, "error writing to dump file %s",
 		      m_filename);
 
+  /* Flush after each line, to ease debugging crashes.  */
+  fflush (m_file);
+
   /* Update line/column: */
   for (const char *ptr = buf; *ptr; ptr++)
     {
@@ -119,7 +123,291 @@  dump::write (const char *fmt, ...)
 recording::location *
 dump::make_location () const
 {
-  return m_ctxt.new_location (m_filename, m_line, m_column);
+  return m_ctxt.new_location (m_filename, m_line, m_column,
+			      /* We need to flag such locations as *not*
+				 created by the user, so that
+				 reproducer::get_identifier can cope with
+				 them appearing *after* the memento that
+				 refers to them.  */
+			      false);
+}
+
+/* A collection of allocations, all of which can be released together, to
+   avoid needing to track and release them individually.  */
+
+class allocator
+{
+ public:
+  ~allocator ();
+
+  char *
+  xstrdup_printf (const char *, ...)
+    ATTRIBUTE_RETURNS_NONNULL
+    GNU_PRINTF(2, 3);
+
+  char *
+  xstrdup_printf_va (const char *, va_list ap)
+    ATTRIBUTE_RETURNS_NONNULL
+    GNU_PRINTF(2, 0);
+
+ private:
+  auto_vec <void *> m_buffers;
+};
+
+/* allocator's destructor.  Call "free" on all of the allocations.  */
+
+allocator::~allocator ()
+{
+  unsigned i;
+  void *buffer;
+  FOR_EACH_VEC_ELT (m_buffers, i, buffer)
+    free (buffer);
+}
+
+/* Formatted printing, allocating to a buffer (or exiting the process if
+   the allocation fails).
+
+   The buffer exists until the allocator is cleaned up, and is freed at
+   that point, so the caller doesn't need to track the result.  */
+
+char *
+allocator::xstrdup_printf (const char *fmt, ...)
+{
+  char *result;
+  va_list ap;
+  va_start (ap, fmt);
+  result = xstrdup_printf_va (fmt, ap);
+  va_end (ap);
+  return result;
+}
+
+/* Formatted printing, allocating to a buffer (or exiting the process if
+   the allocation fails).
+
+   The buffer exists until the allocator is cleaned up, and is freed at
+   that point, so the caller doesn't need to track the result.  */
+
+char *
+allocator::xstrdup_printf_va (const char *fmt, va_list ap)
+{
+  char *result = xvasprintf (fmt, ap);
+  m_buffers.safe_push (result);
+  return result;
+}
+
+/* gcc::jit::reproducer is a subclass of gcc::jit::dump, used for
+   implementing gcc_jit_context_dump_reproducer_to_file.  */
+
+class reproducer : public dump
+{
+ public:
+  reproducer (recording::context &ctxt,
+	      const char *filename);
+
+  void
+  write_params (const vec <recording::context *> &contexts);
+
+  void
+  write_args (const vec <recording::context *> &contexts);
+
+  const char *
+  make_identifier (recording::memento *m, const char *prefix);
+
+  const char *
+  make_tmp_identifier (const char *prefix, recording::memento *m);
+
+  const char *
+  get_identifier (recording::context *ctxt);
+
+  const char *
+  get_identifier (recording::memento *m);
+
+  const char *
+  get_identifier_as_rvalue (recording::rvalue *m);
+
+  const char *
+  get_identifier_as_lvalue (recording::lvalue *m);
+
+  const char *
+  get_identifier_as_type (recording::type *m);
+
+  char *
+  xstrdup_printf (const char *, ...)
+    ATTRIBUTE_RETURNS_NONNULL
+    GNU_PRINTF(2, 3);
+
+ private:
+  hash_map<recording::memento *, const char *> m_identifiers;
+  allocator m_allocator;
+};
+
+/* gcc::jit::reproducer's constructor.  */
+
+reproducer::reproducer (recording::context &ctxt,
+			const char *filename) :
+  dump (ctxt, filename, 0),
+  m_identifiers (),
+  m_allocator ()
+{
+}
+
+/* Write out a list of contexts as a set of parameters within a
+   C function declaration.  */
+
+void
+reproducer::write_params (const vec <recording::context *> &contexts)
+{
+  unsigned i;
+  recording::context *ctxt;
+  FOR_EACH_VEC_ELT (contexts, i, ctxt)
+    {
+      write ("gcc_jit_context *%s",
+	     get_identifier (ctxt));
+      if (i < contexts.length () - 1)
+	write (",\n"
+	       "             ");
+    }
+}
+
+/* Write out a list of contexts as a set of arguments within a call
+   to a C function.  */
+
+void
+reproducer::write_args (const vec <recording::context *> &contexts)
+{
+  unsigned i;
+  recording::context *ctxt;
+  FOR_EACH_VEC_ELT (contexts, i, ctxt)
+    {
+      write ("%s",
+	     get_identifier (ctxt));
+      if (i < contexts.length () - 1)
+	write (",\n"
+	       "               ");
+    }
+}
+
+/* Generate a C identifier for the given memento, associating the generated
+   buffer with the memento (for future calls to get_identifier et al).
+
+   The reproducer will eventually clean up the buffer in its dtor.  */
+const char *
+reproducer::make_identifier (recording::memento *m, const char *prefix)
+{
+  char *result;
+  if (strlen (m->get_debug_string ()) < 100)
+    {
+      result = m_allocator.xstrdup_printf ("%s_%s_%p",
+					   prefix,
+					   m->get_debug_string (),
+					   (void *) m);
+      for (char *p = result; *p; p++)
+	if (!ISALNUM (*p))
+	  *p = '_';
+    }
+  else
+    result = m_allocator.xstrdup_printf ("%s_%p",
+					 prefix, (void *) m);
+  m_identifiers.put (m, result);
+  return result;
+}
+
+/* Generate a C identifier for a temporary variable.
+   The reproducer will eventually clean up the buffer in its dtor.  */
+
+const char *
+reproducer::make_tmp_identifier (const char *prefix, recording::memento *m)
+{
+  return m_allocator.xstrdup_printf ("%s_%s",
+				     prefix, get_identifier (m));
+}
+
+/* Generate a C identifier for the given context.
+   The reproducer will eventually clean up the buffer in its dtor.  */
+
+const char *
+reproducer::get_identifier (recording::context *ctxt)
+{
+  return m_allocator.xstrdup_printf ("ctxt_%p",
+				     (void *)ctxt);
+}
+
+/* Locate the C identifier for the given memento, which is assumed to
+   have already been created via make_identifier.  */
+
+const char *
+reproducer::get_identifier (recording::memento *m)
+{
+  if (!m)
+    return "NULL";
+
+  /* gcc_jit_context_dump_to_file (, , 1) generates and writes locations,
+     and hence these locations appear in the context's memento list
+     out-of-order: they appear in the context's memento list *after*
+     the memento that refers to them.  For this case, it's simplest to
+     pretend that they're NULL when writing out the code to recreate the
+     memento that uses them.  */
+  if (recording::location *loc = m->dyn_cast_location ())
+    if (!loc->created_by_user ())
+      return "NULL";
+
+  const char **slot = m_identifiers.get (m);
+  if (!slot)
+    {
+      get_context ().add_error (NULL,
+				"unable to find identifier for %p: %s",
+				(void *)m,
+				m->get_debug_string ());
+      gcc_unreachable ();
+    }
+  return *slot;
+}
+
+/* Locate the C identifier for the given rvalue, wrapping it within
+   a gcc_*_as_rvalue upcast if necessary.  */
+
+const char *
+reproducer::get_identifier_as_rvalue (recording::rvalue *m)
+{
+  return m->access_as_rvalue (*this);
+}
+
+/* Locate the C identifier for the given lvalue, wrapping it within
+   a gcc_*_as_lvalue upcast if necessary.  */
+
+const char *
+reproducer::get_identifier_as_lvalue (recording::lvalue *m)
+{
+  return m->access_as_lvalue (*this);
+}
+
+/* Locate the C identifier for the given type, wrapping it within
+   a gcc_*_as_type upcast if necessary.  */
+
+const char *
+reproducer::get_identifier_as_type (recording::type *m)
+{
+  return m->access_as_type (*this);
+}
+
+/* Formatted printing, allocating to a buffer (or exiting the process if
+   the allocation fails).
+
+   The buffer exists until the allocator is cleaned up, and is freed at
+   that point, so the caller doesn't need to track the result.
+
+   Note that we can't use ggc_printf since we're not within the compiler
+   proper (when within gcc_jit_context_dump_reproducer_to_file).  */
+
+char *
+reproducer::xstrdup_printf (const char *fmt, ...)
+{
+  char *result;
+  va_list ap;
+  va_start (ap, fmt);
+  result = m_allocator.xstrdup_printf_va (fmt, ap);
+  va_end (ap);
+  return result;
 }
 
 /**********************************************************************
@@ -170,6 +458,7 @@  recording::playback_block (recording::block *b)
 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_error_count (0),
     m_first_error_str (NULL),
     m_owns_first_error_str (false),
@@ -349,13 +638,15 @@  recording::context::new_string (const char *text)
 
 recording::location *
 recording::context::new_location (const char *filename,
-				 int line,
-				 int column)
+				  int line,
+				  int column,
+				  bool created_by_user)
 {
   recording::location *result =
     new recording::location (this,
-			    new_string (filename),
-			    line, column);
+			     new_string (filename),
+			     line, column,
+			     created_by_user);
   record (result);
   return result;
 }
@@ -1036,6 +1327,196 @@  recording::context::dump_to_file (const char *path, bool update_locations)
     }
 }
 
+static const char * const
+ str_option_reproducer_strings[GCC_JIT_NUM_STR_OPTIONS] = {
+  "GCC_JIT_STR_OPTION_PROGNAME"
+};
+
+static const char * const
+ int_option_reproducer_strings[GCC_JIT_NUM_INT_OPTIONS] = {
+  "GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL"
+};
+
+static const char * const
+ bool_option_reproducer_strings[GCC_JIT_NUM_BOOL_OPTIONS] = {
+  "GCC_JIT_BOOL_OPTION_DEBUGINFO",
+  "GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE",
+  "GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE",
+  "GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE",
+  "GCC_JIT_BOOL_OPTION_DUMP_SUMMARY",
+  "GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING",
+  "GCC_JIT_BOOL_OPTION_SELFCHECK_GC",
+  "GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES"
+};
+
+/* Write C source code to PATH that attempts to replay the API
+   calls made to this context (and its parents), for use in
+   minimizing test cases for libgccjit.
+
+   Implements the post-error-checking part of
+   gcc_jit_context_dump_reproducer_to_file.  */
+
+void
+recording::context::dump_reproducer_to_file (const char *path)
+{
+  JIT_LOG_SCOPE (get_logger ());
+  reproducer r (*this, path);
+
+  /* Generate the "ancestry" of this context, as a list.  */
+  auto_vec <context *> ascending_contexts;
+  for (context *ctxt = this; ctxt; ctxt = ctxt->m_parent_ctxt)
+    ascending_contexts.safe_push (ctxt);
+
+  /* Reverse the list, giving a list of contexts from
+     top-most parent context down through to youngest child context.
+     We will use this list as the parameters of the functions in
+     our generated file.  */
+  unsigned num_ctxts = ascending_contexts.length ();
+  auto_vec <context *> contexts (num_ctxts);
+  for (unsigned i = 0; i < num_ctxts; i++)
+    contexts.safe_push (ascending_contexts[num_ctxts - (i + 1)]);
+
+  /* contexts[0] should be the top-level context.  */
+  gcc_assert (contexts[0]);
+  gcc_assert (contexts[0]->m_toplevel_ctxt == contexts[0]);
+
+  /* The final element in contexts should be "this".  */
+  gcc_assert (contexts[contexts.length () - 1] == this);
+  gcc_assert (contexts[contexts.length () - 1]->m_toplevel_ctxt
+	      == contexts[0]);
+
+  r.write ("/* This code was autogenerated by"
+	   " gcc_jit_context_dump_reproducer_to_file.  */\n\n");
+  r.write ("#include <libgccjit.h>\n\n");
+  r.write ("static void\nset_options (");
+  r.write_params (contexts);
+  r.write (");\n\n");
+  r.write ("static void\ncreate_code (");
+  r.write_params (contexts);
+  r.write (");\n\n");
+  r.write ("int\nmain (int argc, const char **argv)\n");
+  r.write ("{\n");
+  for (unsigned i = 0; i < num_ctxts; i++)
+    r.write ("  gcc_jit_context *%s;\n",
+	     r.get_identifier (contexts[i]));
+  r.write ("  gcc_jit_result *result;\n"
+	   "\n");
+
+  /* Create the contexts.
+     The top-level context is acquired from a clean slate, the others as
+     children of the prior context.  */
+  r.write ("  %s = gcc_jit_context_acquire ();\n",
+	   r.get_identifier (contexts[0]));
+  for (unsigned i = 1; i < num_ctxts; i++)
+    r.write ("  %s = gcc_jit_context_new_child_context (%s);\n",
+	     r.get_identifier (contexts[i]),
+	     r.get_identifier (contexts[i - 1]));
+  r.write ("  set_options (");
+  r.write_args (contexts);
+  r.write (");\n");
+  r.write ("  create_code (");
+  r.write_args (contexts);
+  r.write (");\n");
+
+  r.write ("  result = gcc_jit_context_compile (%s);\n",
+	   r.get_identifier (this));
+
+  for (unsigned i = num_ctxts; i > 0; i--)
+    r.write ("  gcc_jit_context_release (%s);\n",
+	     r.get_identifier (contexts[i - 1]));
+
+  r.write ("  gcc_jit_result_release (result);\n"
+	   "  return 0;\n"
+	   "}\n\n");
+
+  /* Define (char *) variables for use in calls to
+     gcc_jit_context_enable_dump.  */
+  for (unsigned ctxt_idx = 0; ctxt_idx < num_ctxts; ctxt_idx++)
+    {
+      if (m_requested_dumps.length ())
+	{
+	  r.write ("/* Requested dumps for %s.  */\n",
+		   r.get_identifier (contexts[ctxt_idx]));
+	  for (unsigned i = 0; i < m_requested_dumps.length (); i++)
+	    r.write ("static char *dump_%p;\n",
+		     (void *)&m_requested_dumps[i]);
+	  r.write ("\n");
+	}
+    }
+
+  /* Write out values of options.  */
+  r.write ("static void\nset_options (");
+  r.write_params (contexts);
+  r.write (")\n{\n");
+  for (unsigned ctxt_idx = 0; ctxt_idx < num_ctxts; ctxt_idx++)
+    {
+      if (ctxt_idx > 0)
+	r.write ("\n");
+
+      r.write ("  /* Set options for %s.  */\n",
+	       r.get_identifier (contexts[ctxt_idx]));
+
+      r.write ("  /* String options.  */\n");
+      for (int opt_idx = 0; opt_idx < GCC_JIT_NUM_STR_OPTIONS; opt_idx++)
+	r.write ("  gcc_jit_context_set_str_option (%s,\n"
+		 "                                  %s,\n"
+		 "                                  \"%s\");\n",
+		 r.get_identifier (contexts[ctxt_idx]),
+		 str_option_reproducer_strings[opt_idx],
+		 m_str_options[opt_idx] ? m_str_options[opt_idx] : "NULL");
+      r.write ("  /* Int options.  */\n");
+      for (int opt_idx = 0; opt_idx < GCC_JIT_NUM_INT_OPTIONS; opt_idx++)
+	r.write ("  gcc_jit_context_set_int_option (%s,\n"
+		 "                                  %s,\n"
+		 "                                  %i);\n",
+		 r.get_identifier (contexts[ctxt_idx]),
+		 int_option_reproducer_strings[opt_idx],
+		 m_int_options[opt_idx]);
+      r.write ("  /* Boolean options.  */\n");
+      for (int opt_idx = 0; opt_idx < GCC_JIT_NUM_BOOL_OPTIONS; opt_idx++)
+	r.write ("  gcc_jit_context_set_bool_option (%s,\n"
+		 "                                  %s,\n"
+		 "                                  %i);\n",
+		 r.get_identifier (contexts[ctxt_idx]),
+		 bool_option_reproducer_strings[opt_idx],
+		 m_bool_options[opt_idx]);
+
+      if (m_requested_dumps.length ())
+	{
+	  r.write ("  /* Requested dumps.  */\n");
+	  /* Dumpfiles that were requested via gcc_jit_context_enable_dump.  */
+	  for (unsigned i = 0; i < m_requested_dumps.length (); i++)
+	    {
+	      r.write ("  gcc_jit_context_enable_dump (%s,\n"
+		       "                               \"%s\",\n"
+		       "                               &dump_%p);\n",
+		       r.get_identifier (contexts[ctxt_idx]),
+		       m_requested_dumps[i].m_dumpname,
+		       (void *)&m_requested_dumps[i]);
+	    }
+	}
+    }
+  r.write ("}\n\n");
+
+  r.write ("static void\ncreate_code (");
+  r.write_params (contexts);
+  r.write (")\n"
+	   "{\n");
+  for (unsigned ctxt_idx = 0; ctxt_idx < num_ctxts; ctxt_idx++)
+    {
+      memento *m;
+      int i;
+      if (ctxt_idx > 0)
+	r.write ("\n\n");
+
+      r.write ("  /* Replay of API calls for %s.  */\n",
+	       r.get_identifier (contexts[ctxt_idx]));
+      FOR_EACH_VEC_ELT (contexts[ctxt_idx]->m_mementos, i, m)
+	m->write_reproducer (r);
+    }
+  r.write ("}\n");
+}
+
 /* Copy the requested dumps within this context and all ancestors into
    OUT. */
 
@@ -1182,6 +1663,14 @@  recording::string::make_debug_string ()
   return result;
 }
 
+/* Implementation of recording::memento::write_reproducer for strings. */
+
+void
+recording::string::write_reproducer (reproducer &)
+{
+  /* Empty.  */
+}
+
 /* The implementation of class gcc::jit::recording::location.  */
 
 /* Implementation of recording::memento::replay_into for locations.
@@ -1211,6 +1700,23 @@  recording::location::make_debug_string ()
 			      m_filename->c_str (), m_line, m_column);
 }
 
+/* Implementation of recording::memento::write_reproducer for locations. */
+
+void
+recording::location::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "loc");
+  r.write ("  gcc_jit_location *%s =\n"
+	   "    gcc_jit_context_new_location (%s, /* gcc_jit_context *ctxt */\n"
+	   "    %s, /* const char *filename */\n"
+	   "    %i, /* int line */\n"
+	   "    %i);/* int column */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   m_filename->get_debug_string (),
+	   m_line, m_column);
+}
+
 /* The implementation of class gcc::jit::recording::type.  */
 
 /* Given a type T, get the type T*.
@@ -1260,6 +1766,12 @@  recording::type::get_volatile ()
   return result;
 }
 
+const char *
+recording::type::access_as_type (reproducer &r)
+{
+  return r.get_identifier (this);
+}
+
 /* Implementation of pure virtual hook recording::type::dereference for
    recording::memento_of_get_type.  */
 
@@ -1529,6 +2041,42 @@  recording::memento_of_get_type::make_debug_string ()
   return m_ctxt->new_string (get_type_strings[m_kind]);
 }
 
+static const char * const get_type_enum_strings[] = {
+  "GCC_JIT_TYPE_VOID",
+  "GCC_JIT_TYPE_VOID_PTR",
+  "GCC_JIT_TYPE_BOOL",
+  "GCC_JIT_TYPE_CHAR",
+  "GCC_JIT_TYPE_SIGNED_CHAR",
+  "GCC_JIT_TYPE_UNSIGNED_CHAR",
+  "GCC_JIT_TYPE_SHORT",
+  "GCC_JIT_TYPE_UNSIGNED_SHORT",
+  "GCC_JIT_TYPE_INT",
+  "GCC_JIT_TYPE_UNSIGNED_INT",
+  "GCC_JIT_TYPE_LONG",
+  "GCC_JIT_TYPE_UNSIGNED_LONG",
+  "GCC_JIT_TYPE_LONG_LONG",
+  "GCC_JIT_TYPE_UNSIGNED_LONG_LONG",
+  "GCC_JIT_TYPE_FLOAT",
+  "GCC_JIT_TYPE_DOUBLE",
+  "GCC_JIT_TYPE_LONG_DOUBLE",
+  "GCC_JIT_TYPE_CONST_CHAR_PTR",
+  "GCC_JIT_TYPE_SIZE_T",
+  "GCC_JIT_TYPE_FILE_PTR",
+  "GCC_JIT_TYPE_COMPLEX_FLOAT",
+  "GCC_JIT_TYPE_COMPLEX_DOUBLE",
+  "GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE"
+};
+
+void
+recording::memento_of_get_type::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "type");
+  r.write ("  gcc_jit_type *%s = gcc_jit_context_get_type (%s, %s);\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   get_type_enum_strings[m_kind]);
+}
+
 /* The implementation of class gcc::jit::recording::memento_of_get_pointer.  */
 
 /* Override of default implementation of
@@ -1576,6 +2124,26 @@  recording::memento_of_get_pointer::make_debug_string ()
 			      "%s *", m_other_type->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for get_pointer.  */
+
+void
+recording::memento_of_get_pointer::write_reproducer (reproducer &r)
+{
+  /* We need to special-case function pointer types; see the notes in
+     recording::function_type::write_deferred_reproducer.  */
+  if (function_type *fn_type = m_other_type->dyn_cast_function_type ())
+    {
+      fn_type->write_deferred_reproducer (r, this);
+      return;
+    }
+
+  const char *id = r.make_identifier (this, "type");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_type_get_pointer (%s);\n",
+	   id,
+	   r.get_identifier_as_type (m_other_type));
+}
+
 /* The implementation of class gcc::jit::recording::memento_of_get_const.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -1597,6 +2165,18 @@  recording::memento_of_get_const::make_debug_string ()
 			      "const %s", m_other_type->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for const types. */
+
+void
+recording::memento_of_get_const::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "type");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_type_get_const (%s);\n",
+	   id,
+	   r.get_identifier_as_type (m_other_type));
+}
+
 /* The implementation of class gcc::jit::recording::memento_of_get_volatile.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -1618,6 +2198,19 @@  recording::memento_of_get_volatile::make_debug_string ()
 			      "volatile %s", m_other_type->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for volatile
+   types. */
+
+void
+recording::memento_of_get_volatile::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "type");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_type_get_volatile (%s);\n",
+	   id,
+	   r.get_identifier_as_type (m_other_type));
+}
+
 /* The implementation of class gcc::jit::recording::array_type */
 
 /* Implementation of pure virtual hook recording::type::dereference for
@@ -1652,6 +2245,25 @@  recording::array_type::make_debug_string ()
 			      m_num_elements);
 }
 
+/* Implementation of recording::memento::write_reproducer for array
+   types. */
+
+void
+recording::array_type::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "array_type");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_context_new_array_type (%s,\n"
+	   "                                    %s, /* gcc_jit_location *loc */\n"
+	   "                                    %s, /* gcc_jit_type *element_type */\n"
+	   "                                    %i); /* int num_elements */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_type (m_element_type),
+	   m_num_elements);
+}
+
 /* The implementation of class gcc::jit::recording::function_type */
 
 /* Constructor for gcc::jit::recording::function_type.  */
@@ -1775,6 +2387,55 @@  recording::function_type::make_debug_string_with (const char *insert)
   return result;
 }
 
+/* Implementation of recording::memento::write_reproducer for function
+   types.  */
+
+void
+recording::function_type::write_reproducer (reproducer &)
+{
+  /* see notes below.  */
+}
+
+/* There's a get_pointer within context::new_function_ptr_type:
+   the type received by client code isn't the memento for the
+   function_type, but instead the result of get_pointer on it.
+
+   Hence we can't directly write a reproducer that gives function_type.
+   Instead we special-case things within get_pointer, detecting this
+   case, calling the following function.  */
+
+void
+recording::function_type::write_deferred_reproducer (reproducer &r,
+						     memento *ptr_type)
+{
+  gcc_assert (ptr_type);
+  r.make_identifier (this, "function_type");
+  const char *ptr_id = r.make_identifier (ptr_type, "ptr_to");
+  const char *param_types_id = r.make_tmp_identifier ("params_for", this);
+  r.write ("  gcc_jit_type *%s[%i] = {\n",
+	   param_types_id,
+	   m_param_types.length ());
+  int i;
+  type *param_type;
+  FOR_EACH_VEC_ELT (m_param_types, i, param_type)
+    r.write ("    %s,\n", r.get_identifier_as_type (param_type));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_context_new_function_ptr_type (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                           %s, /* gcc_jit_location *loc */\n"
+	   "                                           %s, /* gcc_jit_type *return_type */\n"
+	   "                                           %i, /* int num_params */\n"
+	   "                                           %s, /* gcc_jit_type **param_types */\n"
+	   "                                           %i); /* int is_variadic */\n",
+	   ptr_id,
+	   r.get_identifier (get_context ()),
+	   "NULL", /* location is not stored */
+	   r.get_identifier_as_type (m_return_type),
+	   m_param_types.length (),
+	   param_types_id,
+	   m_is_variadic);
+}
+
 /* The implementation of class gcc::jit::recording::field.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -1811,6 +2472,24 @@  recording::field::make_debug_string ()
   return m_name;
 }
 
+/* Implementation of recording::memento::write_reproducer for fields.  */
+
+void
+recording::field::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "field");
+  r.write("  gcc_jit_field *%s =\n"
+	  "    gcc_jit_context_new_field (%s,\n"
+	  "                               %s, /* gcc_jit_location *loc */\n"
+	  "                               %s, /* gcc_jit_type *type, */\n"
+	  "                               %s); /* const char *name */\n",
+	  id,
+	  r.get_identifier (get_context ()),
+	  r.get_identifier (m_loc),
+	  r.get_identifier_as_type (m_type),
+	  m_name->get_debug_string ());
+}
+
 /* The implementation of class gcc::jit::recording::compound_type */
 
 /* The constructor for gcc::jit::recording::compound_type.  */
@@ -1875,6 +2554,13 @@  recording::struct_::replay_into (replayer *r)
 			  true /* is_struct */));
 }
 
+const char *
+recording::struct_::access_as_type (reproducer &r)
+{
+  return r.xstrdup_printf ("gcc_jit_struct_as_type (%s)",
+			   r.get_identifier (this));
+}
+
 /* Implementation of recording::memento::make_debug_string for
    structs.  */
 
@@ -1885,6 +2571,20 @@  recording::struct_::make_debug_string ()
 			      "struct %s", get_name ()->c_str ());
 }
 
+void
+recording::struct_::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "struct");
+  r.write ("  gcc_jit_struct *%s =\n"
+	   "    gcc_jit_context_new_opaque_struct (%s,\n"
+	   "                                       %s, /* gcc_jit_location *loc */\n"
+	   "                                       %s); /* const char *name */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (get_loc ()),
+	   get_name ()->get_debug_string ());
+}
+
 /* The implementation of class gcc::jit::recording::union_.  */
 
 /* The constructor for gcc::jit::recording::union_.  */
@@ -1918,6 +2618,35 @@  recording::union_::make_debug_string ()
 			      "union %s", get_name ()->c_str ());
 }
 
+/* Implementation of recording::memento::write_reproducer for unions.  */
+
+void
+recording::union_::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "union");
+
+  const char *fields_id = r.make_tmp_identifier ("fields_for", this);
+  r.write ("  gcc_jit_field *%s[%i] = {\n",
+	   fields_id,
+	   get_fields ()->length ());
+  for (int i = 0; i < get_fields ()->length (); i++)
+    r.write ("    %s,\n", r.get_identifier (get_fields ()->get_field (i)));
+  r.write ("  };\n");
+
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_context_new_union_type (%s,\n"
+	   "                                    %s, /* gcc_jit_location *loc */\n"
+	   "                                    %s, /* const char *name */\n"
+	   "                                    %i, /* int num_fields */\n"
+	   "                                    %s); /* gcc_jit_field **fields */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (get_loc ()),
+	   get_name ()->get_debug_string (),
+	   get_fields ()->length (),
+	   fields_id);
+}
+
 /* The implementation of class gcc::jit::recording::fields.  */
 
 /* The constructor for gcc::jit::recording::fields.  */
@@ -1975,6 +2704,38 @@  recording::fields::write_to_dump (dump &d)
   d.write ("};\n");
 }
 
+/* Implementation of recording::memento::write_reproducer for the fields
+   subclass.  */
+
+void
+recording::fields::write_reproducer (reproducer &r)
+{
+  if (m_struct_or_union)
+    if (NULL == m_struct_or_union->dyn_cast_struct ())
+      /* We have a union; the fields have already been written by
+	 union::write_reproducer.  */
+      return;
+
+  const char *fields_id = r.make_identifier (this, "fields");
+  r.write ("  gcc_jit_field *%s[%i] = {\n",
+	   fields_id,
+	   m_fields.length ());
+  int i;
+  field *field;
+  FOR_EACH_VEC_ELT (m_fields, i, field)
+    r.write ("    %s,\n", r.get_identifier (field));
+  r.write ("  };\n");
+
+  r.write ("  gcc_jit_struct_set_fields (%s, /* gcc_jit_struct *struct_type */\n"
+	   "                             %s, /* gcc_jit_location *loc */\n"
+	   "                             %i, /* int num_fields */\n"
+	   "                             %s); /* gcc_jit_field **fields */\n",
+	   r.get_identifier (m_struct_or_union),
+	   r.get_identifier ((memento *)NULL),
+	   m_fields.length (),
+	   fields_id);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    field tables.  */
 
@@ -2143,6 +2904,17 @@  recording::rvalue::set_scope (function *scope)
 }
 
 
+/* Implementation of recording::rvalue::access_as_rvalue for rvalues
+   themselves.
+   Instances of rvalue don't need an upcast call.  */
+
+const char *
+recording::rvalue::access_as_rvalue (reproducer &r)
+{
+  return r.get_identifier (this);
+}
+
+
 /* The implementation of class gcc::jit::recording::lvalue.  */
 
 /* Create a recording::new_access_field_of_lvalue instance and add it to
@@ -2161,6 +2933,26 @@  recording::lvalue::access_field (recording::location *loc,
   return result;
 }
 
+/* Implementation of recording::rvalue::access_as_rvalue for lvalues.
+   Instances of lvalue need to be wrapped in a gcc_jit_lvalue_as_rvalue
+   upcast call.  */
+
+const char *
+recording::lvalue::access_as_rvalue (reproducer &r)
+{
+  return r.xstrdup_printf ("gcc_jit_lvalue_as_rvalue (%s)",
+			   r.get_identifier (this));
+}
+
+/* Implementation of recording::lvalue::access_as_lvalue for lvalues.
+   Instances of lvalue don't need to be upcast.  */
+
+const char *
+recording::lvalue::access_as_lvalue (reproducer &r)
+{
+  return r.get_identifier (this);
+}
+
 /* Create a recording::get_address_of_lvalue instance and add it to
    the lvalue's context's list of mementos.
 
@@ -2189,6 +2981,45 @@  recording::param::replay_into (replayer *r)
 				  m_name->c_str ()));
 }
 
+/* Implementation of recording::rvalue::access_as_rvalue for params.
+   Instances of param need to be wrapped in a gcc_jit_param_as_rvalue
+   upcast call.  */
+
+const char *
+recording::param::access_as_rvalue (reproducer &r)
+{
+  return r.xstrdup_printf ("gcc_jit_param_as_rvalue (%s)",
+			   r.get_identifier (this));
+}
+
+/* Implementation of recording::lvalue::access_as_lvalue for params.
+   Instances of param need to be wrapped in a gcc_jit_param_as_lvalue
+   upcast call.  */
+
+const char *
+recording::param::access_as_lvalue (reproducer &r)
+{
+  return r.xstrdup_printf ("gcc_jit_param_as_lvalue (%s)",
+			   r.get_identifier (this));
+}
+
+/* Implementation of recording::memento::write_reproducer for params. */
+
+void
+recording::param::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "param");
+  r.write ("  gcc_jit_param *%s =\n"
+	   "    gcc_jit_context_new_param (%s,\n"
+	   "                               %s, /* gcc_jit_location *loc */\n"
+	   "                               %s, /*gcc_jit_type *type */\n"
+	   "                               %s); /* const char *name */\n",
+	   id,
+    r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_type (m_type),
+	   m_name->get_debug_string ());
+}
 
 /* The implementation of class gcc::jit::recording::function.  */
 
@@ -2502,6 +3333,63 @@  recording::function::make_debug_string ()
   return m_name;
 }
 
+/* A table of enum gcc_jit_function_kind values expressed in string
+   form.  */
+
+static const char * const names_of_function_kinds[] = {
+  "GCC_JIT_FUNCTION_EXPORTED",
+  "GCC_JIT_FUNCTION_INTERNAL",
+  "GCC_JIT_FUNCTION_IMPORTED",
+  "GCC_JIT_FUNCTION_ALWAYS_INLINE"
+};
+
+/* Implementation of recording::memento::write_reproducer for functions. */
+
+void
+recording::function::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "func");
+
+  if (m_builtin_id)
+    {
+      r.write ("  gcc_jit_function *%s =\n"
+	       "    gcc_jit_context_get_builtin_function (%s,\n"
+	       "                                          %s);\n",
+	       id,
+	       r.get_identifier (get_context ()),
+	       m_name->get_debug_string ());
+      return;
+    }
+  const char *params_id = r.make_tmp_identifier ("params_for", this);
+  r.write ("  gcc_jit_param *%s[%i] = {\n",
+	   params_id,
+	   m_params.length ());
+  int i;
+  param *param;
+  FOR_EACH_VEC_ELT (m_params, i, param)
+    r.write ("    %s,\n", r.get_identifier (param));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_function *%s =\n"
+	   "    gcc_jit_context_new_function (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                  %s, /* gcc_jit_location *loc */\n"
+	   "                                  %s, /* enum gcc_jit_function_kind kind */\n"
+	   "                                  %s, /* gcc_jit_type *return_type */\n"
+	   "                                  %s, /* const char *name */\n"
+	   "                                  %i, /* int num_params */\n"
+	   "                                  %s, /* gcc_jit_param **params */\n"
+	   "                                  %i); /* int is_variadic */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   names_of_function_kinds[m_kind],
+	   r.get_identifier_as_type (m_return_type),
+	   m_name->get_debug_string (),
+	   m_params.length (),
+	   params_id,
+	   m_is_variadic);
+}
+
+
 /* The implementation of class gcc::jit::recording::block.  */
 
 /* Create a recording::eval instance and add it to
@@ -2750,6 +3638,19 @@  recording::block::make_debug_string ()
 				(void *)this);
 }
 
+/* Implementation of recording::memento::write_reproducer for blocks. */
+
+void
+recording::block::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "block");
+  r.write ("  gcc_jit_block *%s =\n"
+	   "    gcc_jit_function_new_block (%s, %s);\n",
+	   id,
+	   r.get_identifier (m_func),
+	   m_name ? m_name->get_debug_string () : "NULL");
+}
+
 /* Dump a block in graphviz form into PP, capturing the block name (if
    any) and the statements.  */
 
@@ -2856,6 +3757,35 @@  recording::global::write_to_dump (dump &d)
 	   get_debug_string ());
 }
 
+/* A table of enum gcc_jit_global_kind values expressed in string
+   form.  */
+
+static const char * const global_kind_reproducer_strings[] = {
+  "GCC_JIT_GLOBAL_EXPORTED",
+  "GCC_JIT_GLOBAL_INTERNAL",
+  "GCC_JIT_GLOBAL_IMPORTED"
+};
+
+/* Implementation of recording::memento::write_reproducer for globals. */
+
+void
+recording::global::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "block");
+  r.write ("  gcc_jit_lvalue *%s =\n"
+    "    gcc_jit_context_new_global (%s, /* gcc_jit_context *ctxt */\n"
+    "                                %s, /* gcc_jit_location *loc */\n"
+    "                                %s, /* enum gcc_jit_global_kind kind */\n"
+    "                                %s, /* gcc_jit_type *type */\n"
+    "                                %s); /* const char *name */\n",
+    id,
+    r.get_identifier (get_context ()),
+    r.get_identifier (m_loc),
+    global_kind_reproducer_strings[m_kind],
+    r.get_identifier_as_type (get_type ()),
+    m_name->get_debug_string ());
+}
+
 /* The implementation of the various const-handling classes:
    gcc::jit::recording::memento_of_new_rvalue_from_const <HOST_TYPE>.  */
 
@@ -2878,9 +3808,10 @@  memento_of_new_rvalue_from_const <HOST_TYPE>::replay_into (replayer *r)
 					     m_value));
 }
 
-/* The make_debug_string method varies between the various
-   memento_of_new_rvalue_from_const <HOST_TYPE> classes, so we explicitly
-   write specializations of it.
+/* The make_debug_string and write_reproducer methods vary between the
+   various
+     memento_of_new_rvalue_from_const <HOST_TYPE>
+   classes, so we explicitly write specializations of them.
 
    I (dmalcolm) find the code to be clearer if the "recording" vs "playback"
    namespaces are written out explicitly, which is why most of this file
@@ -2908,6 +3839,23 @@  memento_of_new_rvalue_from_const <int>::make_debug_string ()
 			      m_value);
 }
 
+/* The write_reproducer specialization for <int>.  */
+
+template <>
+void
+memento_of_new_rvalue_from_const <int>::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+    "    gcc_jit_context_new_rvalue_from_int (%s, /* gcc_jit_context *ctxt */\n"
+    "                                         %s, /* gcc_jit_type *numeric_type */\n"
+    "                                         %i); /* int value */\n",
+    id,
+    r.get_identifier (get_context ()),
+    r.get_identifier_as_type (m_type),
+    m_value);
+}
+
 /* The make_debug_string specialization for <long>, rendering it as
      (TARGET_TYPE)LITERAL
    e.g.
@@ -2923,6 +3871,44 @@  memento_of_new_rvalue_from_const <long>::make_debug_string ()
 			      m_value);
 }
 
+/* The write_reproducer specialization for <long>.  */
+
+template <>
+void
+recording::memento_of_new_rvalue_from_const <long>::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+
+  /* We have to special-case LONG_MIN, since e.g.
+       -9223372036854775808L
+     is parsed as
+       -(9223372036854775808L)
+     and hence we'd get:
+	error: integer constant is so large that it is unsigned [-Werror]
+	Workaround this by writing (LONG_MIN + 1) - 1.  */
+  if (m_value == LONG_MIN)
+    {
+      r.write ("  gcc_jit_rvalue *%s =\n"
+	       "    gcc_jit_context_new_rvalue_from_long (%s, /* gcc_jit_context *ctxt */\n"
+	       "                                          %s, /* gcc_jit_type *numeric_type */\n"
+	       "                                          %ldL - 1); /* long value */\n",
+	       id,
+	       r.get_identifier (get_context ()),
+	       r.get_identifier_as_type (m_type),
+	       m_value + 1);;
+      return;
+    }
+
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_rvalue_from_long (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                          %s, /* gcc_jit_type *numeric_type */\n"
+	   "                                          %ldL); /* long value */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier_as_type (m_type),
+	   m_value);
+	   }
+
 /* The make_debug_string specialization for <double>, rendering it as
      (TARGET_TYPE)LITERAL
    e.g.
@@ -2938,6 +3924,23 @@  memento_of_new_rvalue_from_const <double>::make_debug_string ()
 			      m_value);
 }
 
+/* The write_reproducer specialization for <double>.  */
+
+template <>
+void
+recording::memento_of_new_rvalue_from_const <double>::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+    "    gcc_jit_context_new_rvalue_from_double (%s, /* gcc_jit_context *ctxt */\n"
+    "                                            %s, /* gcc_jit_type *numeric_type */\n"
+    "                                            %f); /* double value */\n",
+    id,
+    r.get_identifier (get_context ()),
+    r.get_identifier_as_type (m_type),
+    m_value);
+}
+
 /* The make_debug_string specialization for <void *>, rendering it as
      (TARGET_TYPE)HEX
    e.g.
@@ -2960,8 +3963,34 @@  memento_of_new_rvalue_from_const <void *>::make_debug_string ()
 				m_type->get_debug_string ());
 }
 
-/* We're done specializing make_debug_string, so we can exit the
-   gcc::jit::recording namespace.  */
+/* Implementation of recording::memento::write_reproducer for <void *>
+   values. */
+
+template <>
+void
+memento_of_new_rvalue_from_const <void *>::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  if (m_value)
+    r.write ("  gcc_jit_rvalue *%s =\n"
+	     "    gcc_jit_context_new_rvalue_from_ptr (%s, /* gcc_jit_context *ctxt */\n"
+	     "                                         %s, /* gcc_jit_type *pointer_type */\n"
+	     "                                         (void *)%p); /* void *value */\n",
+	     id,
+	     r.get_identifier (get_context ()),
+	     r.get_identifier_as_type (m_type),
+	     m_value);
+  else
+    r.write ("  gcc_jit_rvalue *%s =\n"
+	     "    gcc_jit_context_null (%s, /* gcc_jit_context *ctxt */\n"
+	     "                          %s); /* gcc_jit_type *pointer_type */\n",
+	     id,
+	     r.get_identifier (get_context ()),
+	     r.get_identifier_as_type (m_type));
+}
+
+/* We're done specializing make_debug_string and write_reproducer, so we
+   can exit the gcc::jit::recording namespace.  */
 
 } // namespace recording
 
@@ -2986,6 +4015,21 @@  recording::memento_of_new_string_literal::make_debug_string ()
 			      m_value->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for string literal
+   values. */
+
+void
+recording::memento_of_new_string_literal::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+    "    gcc_jit_context_new_string_literal (%s, /* gcc_jit_context *ctxt */\n"
+    "                                        %s); /* const char *value */\n",
+    id,
+    r.get_identifier (get_context ()),
+    m_value->get_debug_string ());
+}
+
 /* The implementation of class gcc::jit::recording::unary_op.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3027,6 +4071,33 @@  recording::unary_op::make_debug_string ()
 			      m_a->get_debug_string ());
 }
 
+static const char * const unary_op_reproducer_strings[] = {
+  "GCC_JIT_UNARY_OP_MINUS",
+  "GCC_JIT_UNARY_OP_BITWISE_NEGATE",
+  "GCC_JIT_UNARY_OP_LOGICAL_NEGATE",
+  "GCC_JIT_UNARY_OP_ABS"
+};
+
+/* Implementation of recording::memento::write_reproducer for unary ops.  */
+
+void
+recording::unary_op::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_unary_op (%s,\n"
+	   "                                  %s, /* gcc_jit_location *loc */\n"
+	   "                                  %s, /* enum gcc_jit_unary_op op */\n"
+	   "                                  %s, /* gcc_jit_type *result_type */\n"
+	   "                                  %s); /* gcc_jit_rvalue *a */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   unary_op_reproducer_strings[m_op],
+	   r.get_identifier_as_type (get_type ()),
+	   r.get_identifier_as_rvalue (m_a));
+}
+
 /* The implementation of class gcc::jit::recording::binary_op.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3079,6 +4150,43 @@  recording::binary_op::make_debug_string ()
 			      m_b->get_debug_string ());
 }
 
+static const char * const binary_op_reproducer_strings[] = {
+  "GCC_JIT_BINARY_OP_PLUS",
+  "GCC_JIT_BINARY_OP_MINUS",
+  "GCC_JIT_BINARY_OP_MULT",
+  "GCC_JIT_BINARY_OP_DIVIDE",
+  "GCC_JIT_BINARY_OP_MODULO",
+  "GCC_JIT_BINARY_OP_BITWISE_AND",
+  "GCC_JIT_BINARY_OP_BITWISE_XOR",
+  "GCC_JIT_BINARY_OP_BITWISE_OR",
+  "GCC_JIT_BINARY_OP_LOGICAL_AND",
+  "GCC_JIT_BINARY_OP_LOGICAL_OR",
+  "GCC_JIT_BINARY_OP_LSHIFT",
+  "GCC_JIT_BINARY_OP_RSHIFT"
+};
+
+/* Implementation of recording::memento::write_reproducer for binary ops.  */
+
+void
+recording::binary_op::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_binary_op (%s,\n"
+	   "                                   %s, /* gcc_jit_location *loc */\n"
+	   "                                   %s, /* enum gcc_jit_binary_op op */\n"
+	   "                                   %s, /* gcc_jit_type *result_type */\n"
+	   "                                   %s, /* gcc_jit_rvalue *a */\n"
+	   "                                   %s); /* gcc_jit_rvalue *b */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   binary_op_reproducer_strings[m_op],
+	   r.get_identifier_as_type (get_type ()),
+	   r.get_identifier_as_rvalue (m_a),
+	   r.get_identifier_as_rvalue (m_b));
+}
+
 /* The implementation of class gcc::jit::recording::comparison.  */
 
 /* Implementation of recording::memento::make_debug_string for
@@ -3104,6 +4212,39 @@  recording::comparison::make_debug_string ()
 			      m_b->get_debug_string ());
 }
 
+/* A table of enum gcc_jit_comparison values expressed in string
+   form.  */
+
+static const char * const comparison_reproducer_strings[] =
+{
+  "GCC_JIT_COMPARISON_EQ",
+  "GCC_JIT_COMPARISON_NE",
+  "GCC_JIT_COMPARISON_LT",
+  "GCC_JIT_COMPARISON_LE",
+  "GCC_JIT_COMPARISON_GT",
+  "GCC_JIT_COMPARISON_GE"
+};
+
+/* Implementation of recording::memento::write_reproducer for comparisons.  */
+
+void
+recording::comparison::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_comparison (%s,\n"
+	   "                                    %s, /* gcc_jit_location *loc */\n"
+	   "                                    %s, /* enum gcc_jit_comparison op */\n"
+	   "                                    %s, /* gcc_jit_rvalue *a */\n"
+	   "                                    %s); /* gcc_jit_rvalue *b */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   comparison_reproducer_strings[m_op],
+	   r.get_identifier_as_rvalue (m_a),
+	   r.get_identifier_as_rvalue (m_b));
+}
+
 /* Implementation of pure virtual hook recording::memento::replay_into
    for recording::comparison.  */
 
@@ -3157,6 +4298,24 @@  recording::cast::make_debug_string ()
 			      m_rvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for casts.  */
+
+void
+recording::cast::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_cast (%s,\n"
+	   "                              %s, /* gcc_jit_location *loc */\n"
+	   "                              %s, /* gcc_jit_rvalue *rvalue */\n"
+	   "                              %s); /* gcc_jit_type *type */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_rvalue (m_rvalue),
+	   r.get_identifier_as_type (get_type ()));
+}
+
 /* The implementation of class gcc::jit::recording::call.  */
 
 /* The constructor for gcc::jit::recording::call.  */
@@ -3242,6 +4401,31 @@  recording::call::make_debug_string ()
   return result;
 }
 
+void
+recording::call::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "call");
+  const char *args_id = r.make_tmp_identifier ("args_for_", this);
+  r.write ("  gcc_jit_rvalue *%s[%i] = {\n",
+	   args_id,
+	   m_args.length ());
+  for (unsigned i = 0; i< m_args.length (); i++)
+    r.write ("    %s,\n", r.get_identifier_as_rvalue (m_args[i]));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_call (%s, /* gcc_jit_context *ctxt */\n"
+	   "                              %s, /* gcc_jit_location *loc */\n"
+	   "                              %s, /* gcc_jit_function *func */\n"
+	   "                              %i, /* int numargs  */ \n"
+	   "                              %s); /* gcc_jit_rvalue **args*/\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier (m_func),
+	   m_args.length (),
+	   args_id);
+}
+
 /* The implementation of class gcc::jit::recording::call_through_ptr.  */
 
 /* The constructor for recording::call_through_ptr. */
@@ -3330,6 +4514,34 @@  recording::call_through_ptr::make_debug_string ()
   return result;
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   call_through_ptr.  */
+
+void
+recording::call_through_ptr::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "call");
+  const char *args_id = r.make_tmp_identifier ("args_for_", this);
+  r.write ("  gcc_jit_rvalue *%s[%i] = {\n",
+	     args_id,
+	     m_args.length ());
+  for (unsigned i = 0; i< m_args.length (); i++)
+    r.write ("    %s,\n", r.get_identifier_as_rvalue (m_args[i]));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_call_through_ptr (%s, /* gcc_jit_context *ctxt */\n"
+	   "                              %s, /* gcc_jit_location *loc */\n"
+	   "                              %s, /* gcc_jit_rvalue *fn_ptr */\n"
+	   "                              %i, /* int numargs  */ \n"
+	   "                              %s); /* gcc_jit_rvalue **args*/\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_rvalue (m_fn_ptr),
+	   m_args.length (),
+	   args_id);
+}
+
 /* The implementation of class gcc::jit::recording::array_access.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3366,6 +4578,25 @@  recording::array_access::make_debug_string ()
 			      m_index->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   array_access.  */
+
+void
+recording::array_access::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "lvalue");
+  r.write ("  gcc_jit_lvalue *%s = \n"
+	   "    gcc_jit_context_new_array_access (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                      %s, /*gcc_jit_location *loc */\n"
+	   "                                      %s, /* gcc_jit_rvalue *ptr */\n"
+	   "                                      %s); /* gcc_jit_rvalue *index */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_rvalue (m_ptr),
+	   r.get_identifier_as_rvalue (m_index));
+}
+
 /* The implementation of class gcc::jit::recording::access_field_of_lvalue.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3402,6 +4633,23 @@  recording::access_field_of_lvalue::make_debug_string ()
 			      m_field->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   access_field_of_lvalue.  */
+
+void
+recording::access_field_of_lvalue::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "lvalue");
+  r.write ("  gcc_jit_lvalue *%s = \n"
+	   "    gcc_jit_lvalue_access_field (%s, /*gcc_jit_lvalue *struct_or_union */\n"
+	   "                                 %s, /*gcc_jit_location *loc */\n"
+	   "                                 %s);\n",
+	   id,
+	   r.get_identifier_as_lvalue (m_lvalue),
+	   r.get_identifier (m_loc),
+	   r.get_identifier (m_field));
+}
+
 /* The implementation of class gcc::jit::recording::access_field_rvalue.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3437,6 +4685,23 @@  recording::access_field_rvalue::make_debug_string ()
 			      m_field->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   access_field_rvalue.  */
+
+void
+recording::access_field_rvalue::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  r.write ("  gcc_jit_rvalue *%s = \n"
+	   "    gcc_jit_rvalue_access_field (%s, /*gcc_jit_rvalue *struct_or_union */\n"
+	   "                                 %s, /*gcc_jit_location *loc */\n"
+	   "                                 %s);\n",
+	   id,
+	   r.get_identifier_as_rvalue (m_rvalue),
+	   r.get_identifier (m_loc),
+	   r.get_identifier (m_field));
+}
+
 /* The implementation of class
    gcc::jit::recording::dereference_field_rvalue.  */
 
@@ -3473,6 +4738,23 @@  recording::dereference_field_rvalue::make_debug_string ()
 			      m_field->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   dereference_field_rvalue.  */
+
+void
+recording::dereference_field_rvalue::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "lvalue");
+  r.write ("  gcc_jit_lvalue *%s=\n"
+	   "    gcc_jit_rvalue_dereference_field (%s, /* gcc_jit_rvalue *ptr */\n"
+	   "                                      %s, /* gcc_jit_location *loc */\n"
+	   "                                      %s); /* gcc_jit_field *field */\n",
+	   id,
+	   r.get_identifier_as_rvalue (m_rvalue),
+	   r.get_identifier (m_loc),
+	   r.get_identifier (m_field));
+}
+
 /* The implementation of class gcc::jit::recording::dereference_rvalue.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3506,6 +4788,21 @@  recording::dereference_rvalue::make_debug_string ()
 			      m_rvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   dereference_rvalue.  */
+
+void
+recording::dereference_rvalue::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "dereference");
+  r.write ("  gcc_jit_lvalue *%s =\n"
+	   "    gcc_jit_rvalue_dereference (%s, /* gcc_jit_rvalue *rvalue */\n"
+	   "                                %s); /* gcc_jit_location *loc */\n",
+	   id,
+	   r.get_identifier_as_rvalue (m_rvalue),
+	   r.get_identifier (m_loc));
+}
+
 /* The implementation of class gcc::jit::recording::get_address_of_lvalue.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3539,6 +4836,21 @@  recording::get_address_of_lvalue::make_debug_string ()
 			      m_lvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   get_address_of_lvalue.  */
+
+void
+recording::get_address_of_lvalue::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "address_of");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_lvalue_get_address (%s, /* gcc_jit_lvalue *lvalue */\n"
+	   "                                %s); /* gcc_jit_location *loc */\n",
+	   id,
+	   r.get_identifier_as_lvalue (m_lvalue),
+	   r.get_identifier (m_loc));
+}
+
 /* The implementation of class gcc::jit::recording::local.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3570,6 +4882,22 @@  recording::local::write_to_dump (dump &d)
 	  get_debug_string ());
 }
 
+void
+recording::local::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "local");
+  r.write ("  gcc_jit_lvalue *%s =\n"
+	   "    gcc_jit_function_new_local (%s, /* gcc_jit_function *func */\n"
+	   "                                %s, /* gcc_jit_location *loc */\n"
+	   "                                %s, /* gcc_jit_type *type */\n"
+	   "                                %s); /* const char *name */\n",
+	   id,
+	   r.get_identifier (m_func),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_type (m_type),
+	   m_name->get_debug_string ());
+}
+
 /* The implementation of class gcc::jit::recording::statement.  */
 
 /* We poison the default implementation of
@@ -3624,6 +4952,20 @@  recording::eval::make_debug_string ()
 			      m_rvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   eval statements.  */
+
+void
+recording::eval::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_add_eval (%s, /*gcc_jit_block *block */\n"
+	   "                          %s, /* gcc_jit_location *loc */\n"
+	   "                          %s); /* gcc_jit_rvalue *rvalue */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier_as_rvalue (m_rvalue));
+}
+
 /* The implementation of class gcc::jit::recording::assignment.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3650,6 +4992,22 @@  recording::assignment::make_debug_string ()
 			      m_rvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   assignment statements.  */
+
+void
+recording::assignment::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_add_assignment (%s, /*gcc_jit_block *block */\n"
+	   "                                %s, /* gcc_jit_location *loc */\n"
+	   "                                %s, /* gcc_jit_lvalue *lvalue */\n"
+	   "                                %s); /* gcc_jit_rvalue *rvalue */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier_as_lvalue (m_lvalue),
+	   r.get_identifier_as_rvalue (m_rvalue));
+}
+
 /* The implementation of class gcc::jit::recording::assignment_op.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3687,6 +5045,24 @@  recording::assignment_op::make_debug_string ()
 			      m_rvalue->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   assignment_op statements.  */
+
+void
+recording::assignment_op::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_add_assignment_op (%s, /*gcc_jit_block *block */\n"
+	   "                                   %s, /* gcc_jit_location *loc */\n"
+	   "                                   %s, /* gcc_jit_lvalue *lvalue */\n"
+	   "                                   %s, /* enum gcc_jit_binary_op op */\n"
+	   "                                   %s); /* gcc_jit_rvalue *rvalue */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier_as_lvalue (m_lvalue),
+	   binary_op_reproducer_strings[m_op],
+	   r.get_identifier_as_rvalue (m_rvalue));
+}
+
 /* The implementation of class gcc::jit::recording::comment.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3711,6 +5087,20 @@  recording::comment::make_debug_string ()
 			      m_text->c_str ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   comments.  */
+
+void
+recording::comment::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_add_comment (%s, /*gcc_jit_block *block */\n"
+	   "                             %s, /* gcc_jit_location *loc */\n"
+	   "                             %s); /* const char *text */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   m_text->get_debug_string ());
+}
+
 /* The implementation of class gcc::jit::recording::conditional.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3759,6 +5149,24 @@  recording::conditional::make_debug_string ()
 				m_on_true->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   conditional statements.  */
+
+void
+recording::conditional::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_end_with_conditional (%s, /*gcc_jit_block *block */\n"
+	   "                                      %s, /* gcc_jit_location *loc */\n"
+	   "                                      %s, /* gcc_jit_rvalue *boolval */\n"
+	   "                                      %s, /* gcc_jit_block *on_true */\n"
+	   "                                      %s); /* gcc_jit_block *on_false */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier_as_rvalue (m_boolval),
+	   r.get_identifier (m_on_true),
+	   r.get_identifier (m_on_false));
+}
+
 /* The implementation of class gcc::jit::recording::jump.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3796,6 +5204,20 @@  recording::jump::make_debug_string ()
 			      m_target->get_debug_string ());
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   jump statements.  */
+
+void
+recording::jump::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_block_end_with_jump (%s, /*gcc_jit_block *block */\n"
+	   "                               %s, /* gcc_jit_location *loc */\n"
+	   "                               %s); /* gcc_jit_block *target */\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier (m_target));
+}
+
 /* The implementation of class gcc::jit::recording::return_.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -3836,6 +5258,26 @@  recording::return_::make_debug_string ()
 				"return;");
 }
 
+/* Implementation of recording::memento::write_reproducer for
+   return statements.  */
+
+void
+recording::return_::write_reproducer (reproducer &r)
+{
+  if (m_rvalue)
+    r.write ("  gcc_jit_block_end_with_return (%s, /*gcc_jit_block *block */\n"
+	     "                                 %s, /* gcc_jit_location *loc */\n"
+	     "                                 %s); /* gcc_jit_rvalue *rvalue */\n",
+	     r.get_identifier (get_block ()),
+	     r.get_identifier (get_loc ()),
+	     r.get_identifier_as_rvalue (m_rvalue));
+  else
+    r.write ("  gcc_jit_block_end_with_void_return (%s, /*gcc_jit_block *block */\n"
+	     "                                      %s); /* gcc_jit_location *loc */\n",
+	     r.get_identifier (get_block ()),
+	     r.get_identifier (get_loc ()));
+}
+
 } // namespace gcc::jit
 
 } // namespace gcc
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 812205c..57e7167 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -30,6 +30,7 @@  namespace jit {
 
 class result;
 class dump;
+class reproducer;
 
 /**********************************************************************
  Recording.
@@ -73,7 +74,8 @@  public:
   location *
   new_location (const char *filename,
 		int line,
-		int column);
+		int column,
+		bool created_by_user);
 
   type *
   get_type (enum gcc_jit_types type);
@@ -244,6 +246,8 @@  public:
 
   void dump_to_file (const char *path, bool update_locations);
 
+  void dump_reproducer_to_file (const char *path);
+
   void
   get_all_requested_dumps (vec <recording::requested_dump> *out);
 
@@ -253,6 +257,10 @@  private:
 private:
   context *m_parent_ctxt;
 
+  /* The ultimate ancestor of the contexts within a family tree of
+     contexts.  This has itself as its own m_toplevel_ctxt.  */
+  context *m_toplevel_ctxt;
+
   int m_error_count;
 
   char *m_first_error_str;
@@ -314,6 +322,8 @@  public:
   get_debug_string ();
 
   virtual void write_to_dump (dump &d);
+  virtual void write_reproducer (reproducer &r) = 0;
+  virtual location *dyn_cast_location () { return NULL; }
 
 protected:
   memento (context *ctxt)
@@ -355,6 +365,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   size_t m_len;
@@ -364,11 +375,13 @@  private:
 class location : public memento
 {
 public:
-  location (context *ctxt, string *filename, int line, int column)
+  location (context *ctxt, string *filename, int line, int column,
+	    bool created_by_user)
   : memento (ctxt),
     m_filename (filename),
     m_line (line),
-    m_column (column)
+    m_column (column),
+    m_created_by_user (created_by_user)
  {}
 
   void replay_into (replayer *r);
@@ -400,13 +413,18 @@  public:
     return static_cast <playback::location *> (m_playback_obj);
   }
 
+  location *dyn_cast_location () { return this; }
+  bool created_by_user () const { return m_created_by_user; }
+
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   string *m_filename;
   int m_line;
   int m_column;
+  bool m_created_by_user;
 };
 
 class type : public memento
@@ -458,6 +476,8 @@  public:
     return static_cast <playback::type *> (m_playback_obj);
   }
 
+  virtual const char *access_as_type (reproducer &r);
+
 protected:
   type (context *ctxt)
     : memento (ctxt),
@@ -504,6 +524,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   enum gcc_jit_types m_kind;
@@ -531,6 +552,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   type *m_other_type;
@@ -565,6 +587,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   type *m_other_type;
@@ -593,6 +616,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   type *m_other_type;
@@ -623,6 +647,7 @@  class array_type : public type
 
  private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
  private:
   location *m_loc;
@@ -657,9 +682,14 @@  public:
 
   string * make_debug_string_with_ptr ();
 
+  void
+  write_deferred_reproducer (reproducer &r,
+			     memento *ptr_type);
+
  private:
   string * make_debug_string ();
   string * make_debug_string_with (const char *);
+  void write_reproducer (reproducer &r);
 
 private:
   type *m_return_type;
@@ -698,6 +728,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   location *m_loc;
@@ -757,9 +788,11 @@  public:
 
   void replay_into (replayer *r);
 
+  const char *access_as_type (reproducer &r);
+
 private:
   string * make_debug_string ();
-
+  void write_reproducer (reproducer &r);
 };
 
 // memento of struct_::set_fields
@@ -774,8 +807,12 @@  public:
 
   void write_to_dump (dump &d);
 
+  int length () const { return m_fields.length (); }
+  field *get_field (int i) const { return m_fields[i]; }
+
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   compound_type *m_struct_or_union;
@@ -793,11 +830,11 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   location *m_loc;
   string *m_name;
-  fields *m_fields;
 };
 
 /* An abstract base class for operations that visit all rvalues within an
@@ -861,6 +898,8 @@  public:
   /* Dynamic cast.  */
   virtual param *dyn_cast_param () { return NULL; }
 
+  virtual const char *access_as_rvalue (reproducer &r);
+
 protected:
   location *m_loc;
   type *m_type;
@@ -893,6 +932,9 @@  public:
 
   rvalue *
   as_rvalue () { return this; }
+
+  const char *access_as_rvalue (reproducer &r);
+  virtual const char *access_as_lvalue (reproducer &r);
 };
 
 class param : public lvalue
@@ -920,8 +962,12 @@  public:
 
   param *dyn_cast_param () { return this; }
 
+  const char *access_as_rvalue (reproducer &r);
+  const char *access_as_lvalue (reproducer &r);
+
 private:
   string * make_debug_string () { return m_name; }
+  void write_reproducer (reproducer &r);
 
 private:
   string *m_name;
@@ -978,6 +1024,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   location *m_loc;
@@ -1065,6 +1112,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
   void replay_into (replayer *r);
 
@@ -1103,6 +1151,7 @@  public:
 
 private:
   string * make_debug_string () { return m_name; }
+  void write_reproducer (reproducer &r);
 
 private:
   enum gcc_jit_global_kind m_kind;
@@ -1126,6 +1175,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   HOST_TYPE m_value;
@@ -1146,6 +1196,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   string *m_value;
@@ -1170,6 +1221,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   enum gcc_jit_unary_op m_op;
@@ -1195,6 +1247,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   enum gcc_jit_binary_op m_op;
@@ -1221,6 +1274,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   enum gcc_jit_comparison m_op;
@@ -1245,6 +1299,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
@@ -1265,6 +1320,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   function *m_func;
@@ -1286,6 +1342,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_fn_ptr;
@@ -1310,6 +1367,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_ptr;
@@ -1334,6 +1392,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   lvalue *m_lvalue;
@@ -1358,6 +1417,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
@@ -1382,6 +1442,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
@@ -1403,6 +1464,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
@@ -1424,6 +1486,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   lvalue *m_lvalue;
@@ -1448,6 +1511,7 @@  public:
 
 private:
   string * make_debug_string () { return m_name; }
+  void write_reproducer (reproducer &r);
 
 private:
   function *m_func;
@@ -1495,6 +1559,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
@@ -1515,6 +1580,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   lvalue *m_lvalue;
@@ -1538,6 +1604,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   lvalue *m_lvalue;
@@ -1558,6 +1625,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   string *m_text;
@@ -1583,6 +1651,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_boolval;
@@ -1606,6 +1675,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   block *m_target;
@@ -1627,6 +1697,7 @@  public:
 
 private:
   string * make_debug_string ();
+  void write_reproducer (reproducer &r);
 
 private:
   rvalue *m_rvalue;
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 79320f6..9b55c91 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -106,6 +106,8 @@  namespace gccjit
 		      int flags,
 		      int verbosity);
 
+    void dump_reproducer_to_file (const char *path);
+
     void set_str_option (enum gcc_jit_str_option opt,
 			 const char *value);
 
@@ -559,6 +561,13 @@  context::set_logfile (FILE *logfile,
 }
 
 inline void
+context::dump_reproducer_to_file (const char *path)
+{
+  gcc_jit_context_dump_reproducer_to_file (m_inner_ctxt,
+					   path);
+}
+
+inline void
 context::set_str_option (enum gcc_jit_str_option opt,
 			 const char *value)
 {
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index ad8ee75..a78b3e7 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -386,7 +386,7 @@  gcc_jit_context_new_location (gcc_jit_context *ctxt,
 {
   RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
   JIT_LOG_FUNC (ctxt->get_logger ());
-  return (gcc_jit_location *)ctxt->new_location (filename, line, column);
+  return (gcc_jit_location *)ctxt->new_location (filename, line, column, true);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -2237,6 +2237,22 @@  gcc_jit_context_set_logfile (gcc_jit_context *ctxt,
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
+   gcc::jit::recording::context::dump_reproducer_to_file method in
+   jit-recording.c.  */
+
+void
+gcc_jit_context_dump_reproducer_to_file (gcc_jit_context *ctxt,
+					 const char *path)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
+  ctxt->dump_reproducer_to_file (path);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
    gcc::jit::recording::context::get_first_error method in
    jit-recording.c.  */
 
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 41c76ea..2f41087 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1051,6 +1051,24 @@  gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt);
  Implementation support.
  **********************************************************************/
 
+/* Write C source code into "path" that can be compiled into a
+   self-contained executable (i.e. with libgccjit as the only dependency).
+   The generated code will attempt to replay the API calls that have been
+   made into the given context.
+
+   This may be useful when debugging the library or client code, for
+   reducing a complicated recipe for reproducing a bug into a simpler
+   form.
+
+   Typically you need to supply the option "-Wno-unused-variable" when
+   compiling the generated file (since the result of each API call is
+   assigned to a unique variable within the generated C source, and not
+   all are necessarily then used).  */
+
+extern void
+gcc_jit_context_dump_reproducer_to_file (gcc_jit_context *ctxt,
+					 const char *path);
+
 /* Enable the dumping of a specific set of internal state from the
    compilation, capturing the result in-memory as a buffer.
 
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 3ab88fd..93d5c26 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -33,6 +33,7 @@ 
     gcc_jit_context_acquire;
     gcc_jit_context_compile;
     gcc_jit_context_dump_to_file;
+    gcc_jit_context_dump_reproducer_to_file;
     gcc_jit_context_enable_dump;
     gcc_jit_context_get_builtin_function;
     gcc_jit_context_get_first_error;
diff --git a/gcc/testsuite/jit.dg/harness.h b/gcc/testsuite/jit.dg/harness.h
index 1252af5..6bbe7a7 100644
--- a/gcc/testsuite/jit.dg/harness.h
+++ b/gcc/testsuite/jit.dg/harness.h
@@ -250,6 +250,23 @@  static void set_options (gcc_jit_context *ctxt, const char *argv0)
     0);
 }
 
+/* Concatenate two strings.  The result must be released using "free".  */
+
+char *
+concat_strings (const char *prefix, const char *suffix)
+{
+  char *result = (char *)malloc (strlen (prefix) + strlen (suffix) + 1);
+  if (!result)
+    {
+      fail ("malloc failure");
+      return NULL;
+    }
+  strcpy (result, prefix);
+  strcpy (result + strlen (prefix), suffix);
+  result[strlen (prefix) + strlen (suffix)] = '\0';
+  return result;
+}
+
 #ifndef TEST_ESCHEWS_TEST_JIT
 /* Set up logging to a logfile of the form "test-FOO.exe.log.txt".
 
@@ -271,18 +288,9 @@  set_up_logging (gcc_jit_context *ctxt, const char *argv0)
   FILE *logfile = NULL;
 
   /* Build a logfile name of the form "test-FOO.exe.log.txt".  */
-  logfile_name = (char *)malloc (strlen (argv0)
-				 + strlen (logfile_name_suffix)
-				 + 1);
+  logfile_name = concat_strings (argv0, logfile_name_suffix);
   if (!logfile_name)
-    {
-      fail ("malloc failure");
-      return NULL;
-    }
-  strcpy (logfile_name, argv0);
-  strcpy (logfile_name + strlen (argv0), logfile_name_suffix);
-  logfile_name[strlen (argv0) + strlen (logfile_name_suffix)] = '\0';
-
+    return NULL;
   logfile = fopen (logfile_name, "w");
   CHECK_NON_NULL (logfile);
   free (logfile_name);
@@ -293,6 +301,21 @@  set_up_logging (gcc_jit_context *ctxt, const char *argv0)
   return logfile;
 }
 
+/* Exercise the API entrypoint:
+     gcc_jit_context_dump_reproducer_to_file
+   by calling it on the context, using the path expected by jit.exp.  */
+static void
+dump_reproducer (gcc_jit_context *ctxt, const char *argv0)
+{
+  char *reproducer_name;
+  reproducer_name = concat_strings (argv0, ".reproducer.c");
+  if (!reproducer_name)
+    return;
+  note ("%s: writing reproducer to %s", test, reproducer_name);
+  gcc_jit_context_dump_reproducer_to_file (ctxt, reproducer_name);
+  free (reproducer_name);
+}
+
 /* Run one iteration of the test.  */
 static void
 test_jit (const char *argv0, void *user_data)
@@ -314,6 +337,8 @@  test_jit (const char *argv0, void *user_data)
 
   create_code (ctxt, user_data);
 
+  dump_reproducer (ctxt, argv0);
+
   /* This actually calls into GCC and runs the build, all
      in a mutex for now.  */
   result = gcc_jit_context_compile (ctxt);
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 098ce5e..3caccce 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -301,6 +301,21 @@  set tests [lsort $tests]
 
 verbose "tests: $tests"
 
+# Is testcase NAME meant to generate a reproducer?
+proc is_testcase_meant_to_generate_a_reproducer {name} {
+    # We expect most testcases to generate a reproducer.
+    # The exceptions are the tutorials (which don't have a "test-"
+    # prefix), and test-threads.c (which is unique).
+    verbose "is_testcase_meant_to_generate_a_reproducer: $name"
+    if { [string match "*test-*" $name] } {
+	if { [string match "*test-threads.c" $name] } {
+	    return 0
+	}
+	return 1
+    }
+    return 0
+}
+
 # libgloss has found the driver (as "xgcc" or "gcc) and stored
 # its full path as GCC_UNDER_TEST.
 proc get_path_of_driver {} {
@@ -365,6 +380,14 @@  proc jit-dg-test { prog do_what extra_tool_flags } {
       return
     }
 
+    # Most of the test cases use gcc_jit_context_dump_reproducer_to_file
+    # as they run to write out a .c file that reproduces their behavior,
+    # exercising that API.
+    set generated_reproducer "${output_file}.reproducer.c"
+
+    # Delete any such generated .c file from a previous run.
+    catch "exec rm -f $generated_reproducer"
+
     # Run the test executable, capturing the PASS/FAIL textual output
     # from the C API, converting it into the Tcl API.
 
@@ -456,6 +479,54 @@  proc jit-dg-test { prog do_what extra_tool_flags } {
 
     restore_ld_library_path_env_vars
 
+    # Most of the test cases use gcc_jit_context_dump_reproducer_to_file
+    # as they run to write out a .c file that reproduces their behavior,
+    # exercising that API.
+
+    if { [is_testcase_meant_to_generate_a_reproducer $name] } {
+	verbose "$name is meant to generate a reproducer"
+	# Verify that a reproducer was generated
+	if { [file exists $generated_reproducer] == 1} {
+	    pass "found generated reproducer: $generated_reproducer"
+	    set output_file "${generated_reproducer}.exe"
+	    # (this overwrites output_file)
+
+	    # Try to compile the generated reproducer
+	    verbose "compilation_function=$compilation_function"
+
+	    # The .c file written by gcc_jit_context_dump_reproducer_to_file
+	    # assigns the result of each API call to a unique variable, and not
+	    # all are necessarily used, so we need -Wno-unused-variable.
+	    set options \
+		"{additional_flags=$extra_tool_flags -Wno-unused-variable}"
+	    verbose "options=$options"
+
+	    set comp_output2 [$compilation_function $generated_reproducer \
+				  $output_file "executable" $options]
+	    if ![jit_check_compile "generated reproducer from $name" "initial compilation" \
+		     $output_file $comp_output2] then {
+			 return
+		     }
+
+	    # The caller, dg-test, will verify comp_output, which contains
+	    # the output from compiling the testcase and will issue a fail
+	    # if it's non-empty (e.g. containing warnings, the
+	    # "test for excess errors").
+	    #
+	    # Append the output from compiling the reproducer, so that this is also
+	    # verified:
+	    append comp_output $comp_output2
+
+	    # TODO: we should try to run the built executable
+	    # It's not quite a quine, since it embeds ptrs which could change
+	    # from run to run.
+	} else {
+	    fail "did not find a generated reproducer: $generated_reproducer"
+	}
+    } else {
+	verbose "$name is not meant to generate a reproducer"
+    }
+
     return [list $comp_output $output_file]
 }
 
diff --git a/gcc/testsuite/jit.dg/test-nested-contexts.c b/gcc/testsuite/jit.dg/test-nested-contexts.c
index 81b1bb2..451d69b 100644
--- a/gcc/testsuite/jit.dg/test-nested-contexts.c
+++ b/gcc/testsuite/jit.dg/test-nested-contexts.c
@@ -626,6 +626,14 @@  main (int argc, char **argv)
 					    "dump-of-test-nested-contexts-bottom.c",
 					    1);
 
+	      /* Dump a reproducer for the bottom context.
+		 The generated reproducer needs to also regenerate the
+		 parent contexts, so this gives us test coverage for
+		 that case.  */
+	      gcc_jit_context_dump_reproducer_to_file (
+		bottom_level.ctxt,
+		"test-nested-contexts.c.exe.reproducer.c");
+
 	      gcc_jit_result *bottom_result =
 		gcc_jit_context_compile (bottom_level.ctxt);
 	      verify_bottom_code (bottom_level.ctxt, bottom_result);