@@ -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
-------
@@ -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.
@@ -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, \
@@ -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);
@@ -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
@@ -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;
@@ -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)
{
@@ -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. */
@@ -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.
@@ -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;
@@ -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);
@@ -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]
}
@@ -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);