diff mbox

[jit] New API entrypoint: gcc_jit_context_dump_to_file

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

Commit Message

David Malcolm Feb. 21, 2014, 3:56 p.m. UTC
Committed to branch dmalcolm/jit:

Add a new "gcc_jit_context_dump_to_file", which dumps a C-like
representation of the context's IR to a given path.

There is also a flag "update_locations", which, when true, will set up
gcc_jit_location information throughout the context, pointing at the dump
file as if it were a source file.

I've been using this in conjunction with GCC_JIT_BOOL_OPTION_DEBUGINFO to
step through generated code in the debugger (when trying to debug my port
of GNU Octave's JIT to libgccjit).

gcc/jit/
	* libgccjit.h (gcc_jit_context_dump_to_file): New.
	* libgccjit.map (gcc_jit_context_dump_to_file): New.
	* libgccjit.c (gcc_jit_context_dump_to_file): New.
	* libgccjit++.h (gccjit::context::dump_to_file): New.

	* internal-api.h (gcc::jit::dump): New class.
	(gcc::jit::recording::playback_location): Add a replayer argument,
	so that playback locations can be created before playback statements.
	(gcc::jit::recording::location::playback_location): Likewise.
	(gcc::jit::recording::statement::playback_location): Likewise.
	(gcc::jit::recording::context::dump_to_file): New.
	(gcc::jit::recording::context::m_structs): New field, for use by
	dump_to_file.
	(gcc::jit::recording::context::m_functions): Likewise.
	(gcc::jit::recording::memento::write_to_dump): New virtual function.
	(gcc::jit::recording::field::write_to_dump): New.
	(gcc::jit::recording::fields::write_to_dump): New.
	(gcc::jit::recording::function::write_to_dump): New.
	(gcc::jit::recording::function::m_locals): New field for use by
	write_to_dump.
	(gcc::jit::recording::function::m_activity): Likewise.
	(gcc::jit::recording::local::write_to_dump): New.
	(gcc::jit::recording::statement::write_to_dump): New.
	(gcc::jit::recording::place_label::write_to_dump): New.

	* internal-api.c (gcc::jit::dump::dump): New.
	(gcc::jit::dump::~dump): New.
	(gcc::jit::dump::write): New.
	(gcc::jit::dump::make_location): New.
	(gcc::jit::recording::playback_location): Add a replayer argument,
	so that playback locations can be created before playback statements.

	(gcc::jit::recording::context::context): Initialize new fields.
	(gcc::jit::recording::function::function): Likewise.

	(gcc::jit::recording::context::new_struct_type): Add struct to the
	context's m_structs vector.
	(gcc::jit::recording::context::new_function): Add function to the
	context's m_functions vector.
	(gcc::jit::recording::context::dump_to_file): New.
	(gcc::jit::recording::memento::write_to_dump): New.
	(gcc::jit::recording::field::write_to_dump): New.
	(gcc::jit::recording::fields::write_to_dump): New.
	(gcc::jit::recording::function::write_to_dump): New.
	(gcc::jit::recording::local::write_to_dump): New.
	(gcc::jit::recording::statement::write_to_dump): New.
	(gcc::jit::recording::place_label::write_to_dump): New.

	(gcc::jit::recording::array_type::replay_into): Pass on replayer
	to call to playback_location.
	(gcc::jit::recording::field::replay_into): Likewise.
	(gcc::jit::recording::struct_::replay_into): Likewise.
	(gcc::jit::recording::param::replay_into): Likewise.
	(gcc::jit::recording::function::replay_into): Likewise.
	(gcc::jit::recording::global::replay_into): Likewise.
	(gcc::jit::recording::unary_op::replay_into): Likewise.
	(gcc::jit::recording::binary_op::replay_into): Likewise.
	(gcc::jit::recording::comparison::replay_into): Likewise.
	(gcc::jit::recording::call::replay_into): Likewise.
	(gcc::jit::recording::array_access::replay_into): Likewise.
	(gcc::jit::recording::access_field_of_lvalue::replay_into): Likewise.
	(gcc::jit::recording::access_field_rvalue::replay_into): Likewise.
	(gcc::jit::recording::dereference_field_rvalue::replay_into): Likewise.
	(gcc::jit::recording::dereference_rvalue::replay_into): Likewise.
	(gcc::jit::recording::get_address_of_lvalue::replay_into): Likewise.
	(gcc::jit::recording::local::replay_into): Likewise.
	(gcc::jit::recording::eval::replay_into): Likewise.
	(gcc::jit::recording::assignment::replay_into): Likewise.
	(gcc::jit::recording::assignment_op::replay_into): Likewise.
	(gcc::jit::recording::comment::replay_into): Likewise.
	(gcc::jit::recording::conditional::replay_into): Likewise.
	(gcc::jit::recording::place_label::replay_into): Likewise.
	(gcc::jit::recording::jump::replay_into): Likewise.
	(gcc::jit::recording::return_::replay_into): Likewise.
	(gcc::jit::recording::loop::replay_into): Likewise.
	(gcc::jit::recording::loop_end::replay_into): Likewise.

	(gcc::jit::recording::function::new_local): Add to the function's
	vector of locals.
	(gcc::jit::recording::function::add_eval): Add to the function's
	m_activity field.
	(gcc::jit::recording::function::add_assignment): Likewise.
	(gcc::jit::recording::function::add_assignment_op): Likewise.
	(gcc::jit::recording::function::add_comment): Likewise.
	(gcc::jit::recording::function::add_conditional): Likewise.
	(gcc::jit::recording::function::place_forward_label): Likewise.
	(gcc::jit::recording::function::add_jump): Likewise.
	(gcc::jit::recording::function::add_return): Likewise.
	(gcc::jit::recording::function::new_loop): Likewise.

	(gcc::jit::recording::conditional::make_debug_string): Add missing
	semicolon.
---
 gcc/jit/ChangeLog.jit  |  95 ++++++++++++++++
 gcc/jit/internal-api.c | 298 +++++++++++++++++++++++++++++++++++++++++--------
 gcc/jit/internal-api.h |  80 ++++++++++++-
 gcc/jit/libgccjit++.h  |  12 ++
 gcc/jit/libgccjit.c    |  10 ++
 gcc/jit/libgccjit.h    |  13 +++
 gcc/jit/libgccjit.map  |   1 +
 7 files changed, 458 insertions(+), 51 deletions(-)
diff mbox

Patch

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index 0978a9c..006881d 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,98 @@ 
+2014-02-21  David Malcolm  <dmalcolm@redhat.com>
+
+	* libgccjit.h (gcc_jit_context_dump_to_file): New.
+	* libgccjit.map (gcc_jit_context_dump_to_file): New.
+	* libgccjit.c (gcc_jit_context_dump_to_file): New.
+	* libgccjit++.h (gccjit::context::dump_to_file): New.
+
+	* internal-api.h (gcc::jit::dump): New class.
+	(gcc::jit::recording::playback_location): Add a replayer argument,
+	so that playback locations can be created before playback statements.
+	(gcc::jit::recording::location::playback_location): Likewise.
+	(gcc::jit::recording::statement::playback_location): Likewise.
+	(gcc::jit::recording::context::dump_to_file): New.
+	(gcc::jit::recording::context::m_structs): New field, for use by
+	dump_to_file.
+	(gcc::jit::recording::context::m_functions): Likewise.
+	(gcc::jit::recording::memento::write_to_dump): New virtual function.
+	(gcc::jit::recording::field::write_to_dump): New.
+	(gcc::jit::recording::fields::write_to_dump): New.
+	(gcc::jit::recording::function::write_to_dump): New.
+	(gcc::jit::recording::function::m_locals): New field for use by
+	write_to_dump.
+	(gcc::jit::recording::function::m_activity): Likewise.
+	(gcc::jit::recording::local::write_to_dump): New.
+	(gcc::jit::recording::statement::write_to_dump): New.
+	(gcc::jit::recording::place_label::write_to_dump): New.
+
+	* internal-api.c (gcc::jit::dump::dump): New.
+	(gcc::jit::dump::~dump): New.
+	(gcc::jit::dump::write): New.
+	(gcc::jit::dump::make_location): New.
+	(gcc::jit::recording::playback_location): Add a replayer argument,
+	so that playback locations can be created before playback statements.
+
+	(gcc::jit::recording::context::context): Initialize new fields.
+	(gcc::jit::recording::function::function): Likewise.
+
+	(gcc::jit::recording::context::new_struct_type): Add struct to the
+	context's m_structs vector.
+	(gcc::jit::recording::context::new_function): Add function to the
+	context's m_functions vector.
+	(gcc::jit::recording::context::dump_to_file): New.
+	(gcc::jit::recording::memento::write_to_dump): New.
+	(gcc::jit::recording::field::write_to_dump): New.
+	(gcc::jit::recording::fields::write_to_dump): New.
+	(gcc::jit::recording::function::write_to_dump): New.
+	(gcc::jit::recording::local::write_to_dump): New.
+	(gcc::jit::recording::statement::write_to_dump): New.
+	(gcc::jit::recording::place_label::write_to_dump): New.
+
+	(gcc::jit::recording::array_type::replay_into): Pass on replayer
+	to call to playback_location.
+	(gcc::jit::recording::field::replay_into): Likewise.
+	(gcc::jit::recording::struct_::replay_into): Likewise.
+	(gcc::jit::recording::param::replay_into): Likewise.
+	(gcc::jit::recording::function::replay_into): Likewise.
+	(gcc::jit::recording::global::replay_into): Likewise.
+	(gcc::jit::recording::unary_op::replay_into): Likewise.
+	(gcc::jit::recording::binary_op::replay_into): Likewise.
+	(gcc::jit::recording::comparison::replay_into): Likewise.
+	(gcc::jit::recording::call::replay_into): Likewise.
+	(gcc::jit::recording::array_access::replay_into): Likewise.
+	(gcc::jit::recording::access_field_of_lvalue::replay_into): Likewise.
+	(gcc::jit::recording::access_field_rvalue::replay_into): Likewise.
+	(gcc::jit::recording::dereference_field_rvalue::replay_into): Likewise.
+	(gcc::jit::recording::dereference_rvalue::replay_into): Likewise.
+	(gcc::jit::recording::get_address_of_lvalue::replay_into): Likewise.
+	(gcc::jit::recording::local::replay_into): Likewise.
+	(gcc::jit::recording::eval::replay_into): Likewise.
+	(gcc::jit::recording::assignment::replay_into): Likewise.
+	(gcc::jit::recording::assignment_op::replay_into): Likewise.
+	(gcc::jit::recording::comment::replay_into): Likewise.
+	(gcc::jit::recording::conditional::replay_into): Likewise.
+	(gcc::jit::recording::place_label::replay_into): Likewise.
+	(gcc::jit::recording::jump::replay_into): Likewise.
+	(gcc::jit::recording::return_::replay_into): Likewise.
+	(gcc::jit::recording::loop::replay_into): Likewise.
+	(gcc::jit::recording::loop_end::replay_into): Likewise.
+
+	(gcc::jit::recording::function::new_local): Add to the function's
+	vector of locals.
+	(gcc::jit::recording::function::add_eval): Add to the function's
+	m_activity field.
+	(gcc::jit::recording::function::add_assignment): Likewise.
+	(gcc::jit::recording::function::add_assignment_op): Likewise.
+	(gcc::jit::recording::function::add_comment): Likewise.
+	(gcc::jit::recording::function::add_conditional): Likewise.
+	(gcc::jit::recording::function::place_forward_label): Likewise.
+	(gcc::jit::recording::function::add_jump): Likewise.
+	(gcc::jit::recording::function::add_return): Likewise.
+	(gcc::jit::recording::function::new_loop): Likewise.
+
+	(gcc::jit::recording::conditional::make_debug_string): Add missing
+	semicolon.
+
 2014-02-19  David Malcolm  <dmalcolm@redhat.com>
 
 	* libgccjit.c (gcc_jit_context_new_rvalue_from_ptr): Verify that
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index 37fe70b..957edb7 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -25,15 +25,65 @@ 
 namespace gcc {
 namespace jit {
 
+// class dump
+
+dump::dump (recording::context &ctxt,
+	    const char *filename,
+	    bool update_locations)
+: m_ctxt (ctxt),
+  m_filename (filename),
+  m_update_locations (update_locations),
+  m_line (0),
+  m_column (0)
+{
+  m_file  = fopen (filename, "w");
+}
+
+dump::~dump ()
+{
+  if (m_file)
+    fclose (m_file);
+}
+
+void
+dump::write (const char *fmt, ...)
+{
+  char buf[4096];
+  va_list ap;
+  va_start (ap, fmt);
+  vsnprintf (buf, sizeof (buf), fmt, ap);
+  va_end (ap);
+
+  fwrite (buf, strlen (buf), 1, m_file);
+
+  /* Update line/column: */
+  for (const char *ptr = buf; *ptr; ptr++)
+    {
+      if ('\n' == *ptr)
+	{
+	  m_line++;
+	  m_column = 0;
+	}
+      else
+	m_column++;
+    }
+}
+
+recording::location *
+dump::make_location () const
+{
+  return m_ctxt.new_location (m_filename, m_line, m_column);
+}
+
 /**********************************************************************
  Recording.
  **********************************************************************/
 
 playback::location *
-recording::playback_location (recording::location *loc)
+recording::playback_location (replayer *r, recording::location *loc)
 {
   if (loc)
-    return loc->playback_location ();
+    return loc->playback_location (r);
   else
     return NULL;
 }
@@ -62,6 +112,8 @@  recording::context::context (context *parent_ctxt)
   : m_parent_ctxt (parent_ctxt),
     m_error_count (0),
     m_mementos (),
+    m_structs (),
+    m_functions (),
     m_FILE_type (NULL),
     m_builtins_manager(NULL)
 {
@@ -290,6 +342,7 @@  recording::context::new_struct_type (recording::location *loc,
 {
   recording::struct_ *result = new struct_ (this, loc, new_string (name));
   record (result);
+  m_structs.safe_push (result);
   return result;
 }
 
@@ -321,6 +374,8 @@  recording::context::new_function (recording::location *loc,
 			     num_params, params, is_variadic,
 			     builtin_id);
   record (result);
+  m_functions.safe_push (result);
+
   return result;
 }
 
@@ -548,6 +603,34 @@  recording::context::get_opaque_FILE_type ()
   return m_FILE_type;
 }
 
+void
+recording::context::dump_to_file (const char *path, bool update_locations)
+{
+  int i;
+  dump d (*this, path, update_locations);
+
+  /* Forward declaration of structs.  */
+  struct_ *st;
+  FOR_EACH_VEC_ELT (m_structs, i, st)
+    {
+      d.write ("%s;\n\n", st->get_debug_string ());
+    }
+
+  /* Content of structs, where set.  */
+  FOR_EACH_VEC_ELT (m_structs, i, st)
+    if (st->get_fields ())
+      {
+	st->get_fields ()->write_to_dump (d);
+	d.write ("\n");
+      }
+
+  function *fn;
+  FOR_EACH_VEC_ELT (m_functions, i, fn)
+    {
+      fn->write_to_dump (d);
+    }
+}
+
 /* gcc::jit::recording::memento:: */
 
 const char *
@@ -558,6 +641,12 @@  recording::memento::get_debug_string ()
   return m_debug_string->c_str ();
 }
 
+void
+recording::memento::write_to_dump (dump &d)
+{
+  d.write("  %s\n", get_debug_string ());
+}
+
 /* gcc::jit::recording::string:: */
 recording::string::string (context *ctxt, const char *text)
   : memento (ctxt)
@@ -797,7 +886,7 @@  recording::array_type::dereference ()
 void
 recording::array_type::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_array_type (playback_location (m_loc),
+  set_playback_obj (r->new_array_type (playback_location (r, m_loc),
 				       m_element_type->playback_type (),
 				       m_num_elements));
 }
@@ -903,11 +992,19 @@  recording::function_type::make_debug_string ()
 void
 recording::field::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_field (playback_location (m_loc),
+  set_playback_obj (r->new_field (playback_location (r, m_loc),
 				  m_type->playback_type (),
 				  playback_string (m_name)));
 }
 
+void
+recording::field::write_to_dump (dump &d)
+{
+  d.write ("  %s %s;\n",
+	   m_type->get_debug_string (),
+	   m_name->c_str ());
+}
+
 recording::string *
 recording::field::make_debug_string ()
 {
@@ -947,7 +1044,7 @@  void
 recording::struct_::replay_into (replayer *r)
 {
   set_playback_obj (
-    r->new_struct_type (playback_location (m_loc),
+    r->new_struct_type (playback_location (r, m_loc),
 			m_name->c_str ()));
 }
 
@@ -984,6 +1081,18 @@  recording::fields::replay_into (replayer *)
   m_struct->playback_struct ()->set_fields (playback_fields);
 }
 
+void
+recording::fields::write_to_dump (dump &d)
+{
+  int i;
+  field *f;
+
+  d.write ("%s\n{\n", m_struct->get_debug_string ());
+  FOR_EACH_VEC_ELT (m_fields, i, f)
+    f->write_to_dump (d);
+  d.write ("};\n");
+}
+
 recording::string *
 recording::fields::make_debug_string ()
 {
@@ -1047,7 +1156,7 @@  recording::lvalue::get_address (recording::location *loc)
 void
 recording::param::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_param (playback_location (m_loc),
+  set_playback_obj (r->new_param (playback_location (r, m_loc),
 				  m_type->playback_type (),
 				  m_name->c_str ()));
 }
@@ -1071,7 +1180,9 @@  recording::function::function (context *ctxt,
   m_name (name),
   m_params (),
   m_is_variadic (is_variadic),
-  m_builtin_id (builtin_id)
+  m_builtin_id (builtin_id),
+  m_locals (),
+  m_activity ()
 {
   for (int i = 0; i< num_params; i++)
     m_params.safe_push (params[i]);
@@ -1088,7 +1199,7 @@  recording::function::replay_into (replayer *r)
   FOR_EACH_VEC_ELT (m_params, i, param)
     params.safe_push (param->playback_param ());
 
-  set_playback_obj (r->new_function (playback_location (m_loc),
+  set_playback_obj (r->new_function (playback_location (r, m_loc),
 				     m_kind,
 				     m_return_type->playback_type (),
 				     m_name->c_str (),
@@ -1104,6 +1215,7 @@  recording::function::new_local (recording::location *loc,
 {
   local *result = new local (this, loc, type, new_string (name));
   m_ctxt->record (result);
+  m_locals.safe_push (result);
   return result;
 }
 
@@ -1122,6 +1234,7 @@  recording::function::add_eval (recording::location *loc,
 {
   statement *result = new eval (this, loc, rvalue);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1131,6 +1244,7 @@  recording::function::add_assignment (recording::location *loc,
 {
   statement *result = new assignment (this, loc, lvalue, rvalue);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1141,6 +1255,7 @@  recording::function::add_assignment_op (recording::location *loc,
 {
   statement *result = new assignment_op (this, loc, lvalue, op, rvalue);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1149,6 +1264,7 @@  recording::function::add_comment (recording::location *loc,
 {
   statement *result = new comment (this, loc, new_string (text));
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1159,6 +1275,7 @@  recording::function::add_conditional (recording::location *loc,
 {
   statement *result = new conditional (this, loc, boolval, on_true, on_false);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 recording::label *
@@ -1176,6 +1293,7 @@  recording::function::place_forward_label (recording::location *loc,
 {
   statement *result = new place_label (this, loc, lab);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1184,6 +1302,7 @@  recording::function::add_jump (recording::location *loc,
 {
   statement *result = new jump (this, loc, target);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 void
@@ -1192,6 +1311,7 @@  recording::function::add_return (recording::location *loc,
 {
   statement *result = new return_ (this, loc, rvalue);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
 }
 
 recording::loop *
@@ -1203,9 +1323,67 @@  recording::function::new_loop (recording::location *loc,
   recording::loop *result = new recording::loop (this, loc, boolval,
 						 iteration_var, step);
   m_ctxt->record (result);
+  m_activity.safe_push (result);
   return result;
 }
 
+void
+recording::function::write_to_dump (dump &d)
+{
+  switch (m_kind)
+    {
+    default: gcc_unreachable ();
+    case GCC_JIT_FUNCTION_EXPORTED:
+    case GCC_JIT_FUNCTION_IMPORTED:
+      d.write ("extern ");
+      break;
+    case GCC_JIT_FUNCTION_INTERNAL:
+      d.write ("static ");
+      break;
+     }
+  d.write ("%s\n", m_return_type->get_debug_string ());
+
+  if (d.update_locations ())
+    m_loc = d.make_location ();
+
+  d.write ("%s (", get_debug_string ());
+
+  int i;
+  recording::param *param;
+  FOR_EACH_VEC_ELT (m_params, i, param)
+    {
+      if (i > 0)
+	d.write (", ");
+      d.write ("%s %s",
+	       param->get_type ()->get_debug_string (),
+	       param->get_debug_string ());
+    }
+  d.write (")");
+  if (m_kind == GCC_JIT_FUNCTION_IMPORTED)
+    {
+      d.write ("; /* (imported) */\n\n");
+    }
+  else
+    {
+      int i;
+      local *var = NULL;
+      memento *m;
+      d.write ("\n{\n");
+
+      /* Write locals: */
+      FOR_EACH_VEC_ELT (m_locals, i, var)
+	var->write_to_dump (d);
+      if (m_locals.length ())
+	d.write ("\n");
+
+      /* Write statements and labels: */
+      FOR_EACH_VEC_ELT (m_activity, i, m)
+	m->write_to_dump (d);
+
+      d.write ("}\n\n");
+    }
+}
+
 recording::string *
 recording::function::make_debug_string ()
 {
@@ -1243,7 +1421,7 @@  recording::label::make_debug_string ()
 void
 recording::global::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_global (playback_location (m_loc),
+  set_playback_obj (r->new_global (playback_location (r, m_loc),
 				   m_type->playback_type (),
 				   playback_string (m_name)));
 }
@@ -1321,7 +1499,7 @@  recording::memento_of_new_string_literal::make_debug_string ()
 void
 recording::unary_op::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_unary_op (playback_location (m_loc),
+  set_playback_obj (r->new_unary_op (playback_location (r, m_loc),
 				     m_op,
 				     get_type ()->playback_type (),
 				     m_a->playback_rvalue ()));
@@ -1346,7 +1524,7 @@  recording::unary_op::make_debug_string ()
 void
 recording::binary_op::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_binary_op (playback_location (m_loc),
+  set_playback_obj (r->new_binary_op (playback_location (r, m_loc),
 				      m_op,
 				      get_type ()->playback_type (),
 				      m_a->playback_rvalue (),
@@ -1400,7 +1578,7 @@  recording::comparison::make_debug_string ()
 void
 recording::comparison::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_comparison (playback_location (m_loc),
+  set_playback_obj (r->new_comparison (playback_location (r, m_loc),
 				       m_op,
 				       m_a->playback_rvalue (),
 				       m_b->playback_rvalue ()));
@@ -1427,7 +1605,7 @@  recording::call::replay_into (replayer *r)
   for (unsigned i = 0; i< m_args.length (); i++)
     playback_args.safe_push (m_args[i]->playback_rvalue ());
 
-  set_playback_obj (r->new_call (playback_location (m_loc),
+  set_playback_obj (r->new_call (playback_location (r, m_loc),
 				 m_func->playback_function (),
 				 playback_args));
 }
@@ -1475,7 +1653,7 @@  void
 recording::array_access::replay_into (replayer *r)
 {
   set_playback_obj (
-    r->new_array_access (playback_location (m_loc),
+    r->new_array_access (playback_location (r, m_loc),
 			 m_ptr->playback_rvalue (),
 			 m_index->playback_rvalue ()));
 }
@@ -1490,11 +1668,11 @@  recording::array_access::make_debug_string ()
 }
 
 void
-recording::access_field_of_lvalue::replay_into (replayer *)
+recording::access_field_of_lvalue::replay_into (replayer *r)
 {
   set_playback_obj (
     m_lvalue->playback_lvalue ()
-      ->access_field (playback_location (m_loc),
+      ->access_field (playback_location (r, m_loc),
 		      m_field->playback_field ()));
 
 }
@@ -1509,11 +1687,11 @@  recording::access_field_of_lvalue::make_debug_string ()
 }
 
 void
-recording::access_field_rvalue::replay_into (replayer *)
+recording::access_field_rvalue::replay_into (replayer *r)
 {
   set_playback_obj (
     m_rvalue->playback_rvalue ()
-      ->access_field (playback_location (m_loc),
+      ->access_field (playback_location (r, m_loc),
 		      m_field->playback_field ()));
 }
 
@@ -1527,11 +1705,11 @@  recording::access_field_rvalue::make_debug_string ()
 }
 
 void
-recording::dereference_field_rvalue::replay_into (replayer *)
+recording::dereference_field_rvalue::replay_into (replayer *r)
 {
   set_playback_obj (
     m_rvalue->playback_rvalue ()->
-      dereference_field (playback_location (m_loc),
+      dereference_field (playback_location (r, m_loc),
 			 m_field->playback_field ()));
 }
 
@@ -1545,11 +1723,11 @@  recording::dereference_field_rvalue::make_debug_string ()
 }
 
 void
-recording::dereference_rvalue::replay_into (replayer *)
+recording::dereference_rvalue::replay_into (replayer *r)
 {
   set_playback_obj (
     m_rvalue->playback_rvalue ()->
-      dereference (playback_location (m_loc)));
+      dereference (playback_location (r, m_loc)));
 }
 
 recording::string *
@@ -1561,11 +1739,11 @@  recording::dereference_rvalue::make_debug_string ()
 }
 
 void
-recording::get_address_of_lvalue::replay_into (replayer *)
+recording::get_address_of_lvalue::replay_into (replayer *r)
 {
   set_playback_obj (
     m_lvalue->playback_lvalue ()->
-      get_address (playback_location (m_loc)));
+      get_address (playback_location (r, m_loc)));
 }
 
 recording::string *
@@ -1577,20 +1755,40 @@  recording::get_address_of_lvalue::make_debug_string ()
 }
 
 void
-recording::local::replay_into (replayer *)
+recording::local::replay_into (replayer *r)
 {
   set_playback_obj (
     m_func->playback_function ()
-      ->new_local (playback_location (m_loc),
+      ->new_local (playback_location (r, m_loc),
 		   m_type->playback_type (),
 		   playback_string (m_name)));
 }
 
 void
-recording::eval::replay_into (replayer *)
+recording::local::write_to_dump (dump &d)
+{
+  if (d.update_locations ())
+    m_loc = d.make_location ();
+  d.write("  %s %s;\n",
+	  m_type->get_debug_string (),
+	  get_debug_string ());
+}
+
+// gcc::jit::recording::statement
+
+void
+recording::statement::write_to_dump (dump &d)
+{
+  memento::write_to_dump (d);
+  if (d.update_locations ())
+    m_loc = d.make_location ();
+}
+
+void
+recording::eval::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_eval (playback_location (),
+    ->add_eval (playback_location (r),
 		m_rvalue->playback_rvalue ());
 }
 
@@ -1603,10 +1801,10 @@  recording::eval::make_debug_string ()
 }
 
 void
-recording::assignment::replay_into (replayer *)
+recording::assignment::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_assignment (playback_location (),
+    ->add_assignment (playback_location (r),
 		      m_lvalue->playback_lvalue (),
 		      m_rvalue->playback_rvalue ());
 }
@@ -1627,14 +1825,14 @@  recording::assignment_op::replay_into (replayer *r)
     m_lvalue->playback_lvalue ()->get_type ();
 
   playback::rvalue *binary_op =
-    r->new_binary_op (playback_location (),
+    r->new_binary_op (playback_location (r),
 		      m_op,
 		      result_type,
 		      m_lvalue->playback_rvalue (),
 		      m_rvalue->playback_rvalue ());
 
   playback_function ()
-    ->add_assignment (playback_location (),
+    ->add_assignment (playback_location (r),
 		      m_lvalue->playback_lvalue (),
 		      binary_op);
 }
@@ -1650,10 +1848,10 @@  recording::assignment_op::make_debug_string ()
 }
 
 void
-recording::comment::replay_into (replayer *)
+recording::comment::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_comment (playback_location (),
+    ->add_comment (playback_location (r),
 		   m_text->c_str ());
 }
 
@@ -1666,10 +1864,10 @@  recording::comment::make_debug_string ()
 }
 
 void
-recording::conditional::replay_into (replayer *)
+recording::conditional::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_conditional (playback_location (),
+    ->add_conditional (playback_location (r),
 		       m_boolval->playback_rvalue (),
 		       playback_label (m_on_true),
 		       playback_label (m_on_false));
@@ -1680,7 +1878,7 @@  recording::conditional::make_debug_string ()
 {
   if (m_on_false)
     return string::from_printf (m_ctxt,
-				"if (%s) goto %s else goto %s;",
+				"if (%s) goto %s; else goto %s;",
 				m_boolval->get_debug_string (),
 				m_on_true->get_debug_string (),
 				m_on_false->get_debug_string ());
@@ -1704,10 +1902,10 @@  recording::place_label::place_label (function *func,
 }
 
 void
-recording::place_label::replay_into (replayer *)
+recording::place_label::replay_into (replayer *r)
 {
   playback_function ()
-    ->place_forward_label (playback_location (),
+    ->place_forward_label (playback_location (r),
 			   m_label->playback_label ());
 }
 
@@ -1720,10 +1918,16 @@  recording::place_label::make_debug_string ()
 }
 
 void
-recording::jump::replay_into (replayer *)
+recording::place_label::write_to_dump (dump &d)
+{
+  d.write ("\n%s\n", get_debug_string ());
+}
+
+void
+recording::jump::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_jump (playback_location (),
+    ->add_jump (playback_location (r),
 		m_target->playback_label ());
 }
 
@@ -1736,10 +1940,10 @@  recording::jump::make_debug_string ()
 }
 
 void
-recording::return_::replay_into (replayer *)
+recording::return_::replay_into (replayer *r)
 {
   playback_function ()
-    ->add_return (playback_location (),
+    ->add_return (playback_location (r),
 		  m_rvalue ? m_rvalue->playback_rvalue () : NULL);
 }
 
@@ -1756,11 +1960,11 @@  recording::return_::make_debug_string ()
 }
 
 void
-recording::loop::replay_into (replayer *)
+recording::loop::replay_into (replayer *r)
 {
   set_playback_obj (
     m_func->playback_function ()
-      ->new_loop (playback_location (m_loc),
+      ->new_loop (playback_location (r, m_loc),
 		  m_boolval->playback_rvalue ()));
 }
 
@@ -1789,9 +1993,9 @@  recording::loop::end (location *loc)
 }
 
 void
-recording::loop_end::replay_into (replayer *)
+recording::loop_end::replay_into (replayer *r)
 {
-  m_loop->playback_loop ()->end (playback_location (m_loc));
+  m_loop->playback_loop ()->end (playback_location (r, m_loc));
 }
 
 recording::string *
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index 1be192e..83bda17 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -74,6 +74,7 @@  namespace gcc {
 namespace jit {
 
 class result;
+class dump;
 
 namespace recording {
   class context;
@@ -90,6 +91,7 @@  namespace recording {
   class label;
   class rvalue;
   class lvalue;
+  class local;
   class param;
   class loop;
 }
@@ -112,6 +114,31 @@  namespace playback {
 
 typedef playback::context replayer;
 
+class dump
+{
+public:
+  dump (recording::context &ctxt,
+	const char *filename,
+	bool update_locations);
+  ~dump ();
+
+  void write (const char *fmt, ...)
+    GNU_PRINTF(2, 3);
+
+  bool update_locations () const { return m_update_locations; }
+
+  recording::location *
+  make_location () const;
+
+private:
+  recording::context &m_ctxt;
+  const char *m_filename;
+  bool m_update_locations;
+  int m_line;
+  int m_column;
+  FILE *m_file;
+};
+
 /**********************************************************************
  Recording.
  **********************************************************************/
@@ -119,7 +146,7 @@  typedef playback::context replayer;
 namespace recording {
 
 playback::location *
-playback_location (location *loc);
+playback_location (replayer *r, location *loc);
 
 const char *
 playback_string (string *str);
@@ -282,6 +309,8 @@  public:
 
   type *get_opaque_FILE_type ();
 
+  void dump_to_file (const char *path, bool update_locations);
+
 private:
   context *m_parent_ctxt;
 
@@ -295,6 +324,10 @@  private:
   /* Recorded API usage.  */
   vec<memento *> m_mementos;
 
+  /* Specific recordings, for use by dump_to_file.  */
+  vec<struct_ *> m_structs;
+  vec<function *> m_functions;
+
   type *m_basic_types[NUM_GCC_JIT_TYPES];
   type *m_FILE_type;
 
@@ -325,6 +358,8 @@  public:
   const char *
   get_debug_string ();
 
+  virtual void write_to_dump (dump &d);
+
 protected:
   memento (context *ctxt)
   : m_ctxt (ctxt),
@@ -384,8 +419,29 @@  public:
   void replay_into (replayer *r);
 
   playback::location *
-  playback_location () const
+  playback_location (replayer *r)
   {
+    /* Normally during playback, we can walk forwards through the list of
+       recording objects, playing them back.  The ordering of recording
+       ensures that everything that a recording object refers to has
+       already been played back, so we can simply look up the relevant
+       m_playback_obj.
+
+       Locations are an exception, due to the "write_to_dump" method of
+       recording::statement.  This method can set a new location on a
+       statement after the statement is created, and thus the location
+       appears in the context's memento list *after* the statement that
+       refers to it.
+
+       In such circumstances, the statement is replayed *before* the location,
+       when the latter doesn't yet have a playback object.
+
+       Hence we need to ensure that locations have playback objects.  */
+    if (!m_playback_obj)
+      {
+	replay_into (r);
+      }
+    gcc_assert (m_playback_obj);
     return static_cast <playback::location *> (m_playback_obj);
   }
 
@@ -597,6 +653,8 @@  public:
 
   void replay_into (replayer *);
 
+  void write_to_dump (dump &d);
+
   playback::field *
   playback_field () const
   {
@@ -659,6 +717,8 @@  public:
 
   void replay_into (replayer *r);
 
+  void write_to_dump (dump &d);
+
 private:
   string * make_debug_string ();
 
@@ -839,6 +899,8 @@  public:
   param *get_param (int i) const { return m_params[i]; }
   bool is_variadic () const { return m_is_variadic; }
 
+  void write_to_dump (dump &d);
+
 private:
   string * make_debug_string ();
 
@@ -850,6 +912,9 @@  private:
   vec<param *> m_params;
   int m_is_variadic;
   enum built_in_function m_builtin_id;
+  /* Additional vectors to help when dumping.  */
+  vec<local *> m_locals;
+  vec<memento *> m_activity; // statements and labels
 };
 
 class label : public memento
@@ -1206,6 +1271,8 @@  public:
 
   void replay_into (replayer *r);
 
+  void write_to_dump (dump &d);
+
 private:
   string * make_debug_string () { return m_name; }
 
@@ -1229,12 +1296,15 @@  protected:
   }
 
   playback::location *
-  playback_location () const
+  playback_location (replayer *r) const
   {
-    return ::gcc::jit::recording::playback_location (m_loc);
+    return ::gcc::jit::recording::playback_location (r, m_loc);
   }
 
 private:
+  void write_to_dump (dump &d);
+
+private:
   function *m_func;
   location *m_loc;
 };
@@ -1356,6 +1426,8 @@  public:
 private:
   string * make_debug_string ();
 
+  void write_to_dump (dump &d);
+
 private:
   label *m_label;
 };
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 63f8610..db01053 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -72,6 +72,9 @@  namespace gccjit
 
     gcc_jit_result *compile ();
 
+    void dump_to_file (const std::string &path,
+		       bool update_locations);
+
     void set_int_option (enum gcc_jit_int_option opt,
 			 int value);
 
@@ -472,6 +475,15 @@  context::compile ()
 }
 
 inline void
+context::dump_to_file (const std::string &path,
+		       bool update_locations)
+{
+  gcc_jit_context_dump_to_file (m_inner_ctxt,
+				path.c_str (),
+				update_locations);
+}
+
+inline void
 context::set_int_option (enum gcc_jit_int_option opt,
 			 int value)
 {
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 797785b..7226d81 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -1228,6 +1228,16 @@  gcc_jit_context_compile (gcc_jit_context *ctxt)
   return (gcc_jit_result *)ctxt->compile ();
 }
 
+void
+gcc_jit_context_dump_to_file (gcc_jit_context *ctxt,
+			      const char *path,
+			      int update_locations)
+{
+  RETURN_IF_FAIL (ctxt, NULL, "NULL context");
+  RETURN_IF_FAIL (path, ctxt, "NULL path");
+  ctxt->dump_to_file (path, update_locations);
+}
+
 const char *
 gcc_jit_context_get_first_error (gcc_jit_context *ctxt)
 {
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 3ea6653..f6fbcdf 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -218,6 +218,19 @@  gcc_jit_context_set_bool_option (gcc_jit_context *ctxt,
 extern gcc_jit_result *
 gcc_jit_context_compile (gcc_jit_context *ctxt);
 
+/* To help with debugging: dump a C-like representation to the given path,
+   describing what's been set up on the context.
+
+   If "update_locations" is true, then also set up gcc_jit_location
+   information throughout the context, pointing at the dump file as if it
+   were a source file.  This may be of use in conjunction with
+   GCC_JIT_BOOL_OPTION_DEBUGINFO to allow stepping through the code in a
+   debugger.  */
+extern void
+gcc_jit_context_dump_to_file (gcc_jit_context *ctxt,
+			      const char *path,
+			      int update_locations);
+
 /* To be called after a compile, this gives the first error message
    that occurred on the context.
 
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index fbdca38..3a5bb10 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -4,6 +4,7 @@ 
     # Keep this list sorted alphabetically:
     gcc_jit_context_acquire;
     gcc_jit_context_compile;
+    gcc_jit_context_dump_to_file;
     gcc_jit_context_get_builtin_function;
     gcc_jit_context_get_first_error;
     gcc_jit_context_get_type;