Commit 86d0ac88 by David Malcolm Committed by David Malcolm

jit: New API entrypoint: gcc_jit_context_dump_reproducer_to_file

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

gcc/testsuite/ChangeLog:
	* jit.dg/harness.h (set_up_logging): Move string concatenation
	into...
	(concat_strings): New function.
	(dump_reproducer): New function.
	(test_jit): Call dump_reproducer.
	* jit.dg/jit.exp (is_testcase_meant_to_generate_a_reproducer): New
	function.
	(jit-dg-test): Delete any generated reproducer from previous runs.
	Verify that a generated reproducer was created, and verify that it
	compiles.
	* jit.dg/test-nested-contexts.c (main): Call
	gcc_jit_context_dump_reproducer_to_file.

From-SVN: r219564
parent 9c80f919
2015-01-13 David Malcolm <dmalcolm@redhat.com>
* docs/cp/topics/contexts.rst (Debugging): Add
gccjit::context::dump_reproducer_to_file.
* docs/internals/index.rst (Design notes): New section,
discussing input validation and
gcc_jit_context_dump_reproducer_to_file.
* docs/topics/contexts.rst (Debugging): Add
gcc_jit_context_dump_reproducer_to_file.
* docs/_build/texinfo/libgccjit.texi: Regenerate.
* jit-common.h (gcc::jit::dump::get_context): New accessor.
* jit-recording.c: Include "hash-map.h".
Within namespace ::gcc::jit...
(dump::write): Flush each line.
(dump::make_location): Pass false for new param "created_by_user".
(class allocator): New class.
(allocator::~allocator): New function.
(allocator::xstrdup_printf): New function.
(allocator::xstrdup_printf_va): New function.
(class reproducer): New subclass of dump.
(reproducer::reproducer): New function.
(reproducer::write_params): New function.
(reproducer::write_args): New function.
(reproducer::make_identifier): New function.
(reproducer::make_tmp_identifier): New function.
(reproducer::get_identifier): New pair of functions.
(reproducer::get_identifier_as_rvalue): New function.
(reproducer::get_identifier_as_lvalue): New function.
(reproducer::get_identifier_as_type): New function.
(reproducer::xstrdup_printf): New function.
(recording::context::context): Initialize m_toplevel_ctxt.
(recording::context::new_location): Add param created_by_user.
(str_option_reproducer_strings): New table of strings.
(int_option_reproducer_strings): Likewise.
(bool_option_reproducer_strings): Likewise.
(get_type_enum_strings): Likewise.
(names_of_function_kinds): Likewise.
(global_kind_reproducer_strings): Likewise.
(unary_op_reproducer_strings): Likewise.
(binary_op_reproducer_strings): Likewise.
(comparison_reproducer_strings): Likewise.
Within namespace ::gcc::jit::recording::...
(context::dump_reproducer_to_file): New function.
(string::write_reproducer): Likewise.
(location::write_reproducer): Likewise.
(type::access_as_type): Likewise.
(memento_of_get_type::write_reproducer): Likewise.
(memento_of_get_pointer::write_reproducer): Likewise.
(memento_of_get_const::write_reproducer): Likewise.
(memento_of_get_volatile::write_reproducer): Likewise.
(array_type::write_reproducer): Likewise.
(function_type::write_reproducer): Likewise.
(function_type::write_deferred_reproducer): Likewise.
(field::write_reproducer): Likewise.
(struct_::access_as_type): Likewise.
(struct_::write_reproducer): Likewise.
(union_::write_reproducer): Likewise.
(fields::write_reproducer): Likewise.
(rvalue::access_as_rvalue): Likewise.
(lvalue::access_as_rvalue): Likewise.
(lvalue::access_as_lvalue): Likewise.
(param::access_as_rvalue): Likewise.
(param::access_as_lvalue): Likewise.
(param::write_reproducer): Likewise.
(function::write_reproducer): Likewise.
(block::write_reproducer): Likewise.
(global::write_reproducer): Likewise.
(memento_of_new_rvalue_from_const <int>::write_reproducer):
Likewise.
(memento_of_new_rvalue_from_const <long>::write_reproducer):
Likewise.
(memento_of_new_rvalue_from_const <double>::write_reproducer):
Likewise.
(memento_of_new_rvalue_from_const <void *>::write_reproducer):
Likewise.
(memento_of_new_string_literal::write_reproducer): Likewise.
(unary_op::write_reproducer): Likewise.
(binary_op::write_reproducer): Likewise.
(comparison::write_reproducer): Likewise.
(cast::write_reproducer): Likewise.
(call::write_reproducer): Likewise.
(call_through_ptr::write_reproducer): Likewise.
(array_access::write_reproducer): Likewise.
(access_field_of_lvalue::write_reproducer): Likewise.
(access_field_rvalue::write_reproducer): Likewise.
(dereference_field_rvalue::write_reproducer): Likewise.
(dereference_rvalue::write_reproducer): Likewise.
(get_address_of_lvalue::write_reproducer): Likewise.
(local::write_reproducer): Likewise.
(eval::write_reproducer): Likewise.
(assignment::write_reproducer): Likewise.
(assignment_op::write_reproducer): Likewise.
(comment::write_reproducer): Likewise.
(conditional::write_reproducer): Likewise.
(jump::write_reproducer): Likewise.
(return_::write_reproducer): Likewise.
* jit-recording.h (gcc::jit::reproducer): New forward declararion.
Within namespace ::gcc::jit::recording::...
(context::new_location): Add "created_by_user" param.
(context::dump_reproducer_to_file): New method.
(context::m_toplevel_ctxt): New field.
(memento::write_reproducer): New pure virtual function.
(memento::dyn_cast_location): New virtual function.
(string::write_reproducer):
(location::location): Add "created_by_user" param.
(location::dyn_cast_location): New function.
(location::created_by_user): New accessor.
(location::write_reproducer): New function.
(location::m_created_by_user): New field.
(type::access_as_type): New virtual function.
(location::write_reproducer): Likewise.
(type::access_as_type): Likewise.
(memento_of_get_type::write_reproducer): Likewise.
(memento_of_get_pointer::write_reproducer): Likewise.
(memento_of_get_const::write_reproducer): Likewise.
(memento_of_get_volatile::write_reproducer): Likewise.
(array_type::write_reproducer): Likewise.
(function_type::write_reproducer): Likewise.
(function_type::write_deferred_reproducer): Likewise.
(field::write_reproducer): Likewise.
(struct_::access_as_type): Likewise.
(struct_::write_reproducer): Likewise.
(union_::write_reproducer): Likewise.
(union_::m_fields): Remove stray unused field.
(fields::length): New accessor.
(fields::get_field): New accessor.
(fields::write_reproducer): New function.
(rvalue::access_as_rvalue): Likewise.
(lvalue::access_as_rvalue): Likewise.
(lvalue::access_as_lvalue): Likewise.
(param::access_as_rvalue): Likewise.
(param::access_as_lvalue): Likewise.
(param::write_reproducer): Likewise.
(function::write_reproducer): Likewise.
(block::write_reproducer): Likewise.
(global::write_reproducer): Likewise.
(memento_of_new_rvalue_from_const <HOST_TYPE>::write_reproducer):
Likewise.
(memento_of_new_string_literal::write_reproducer): Likewise.
(unary_op::write_reproducer): Likewise.
(binary_op::write_reproducer): Likewise.
(comparison::write_reproducer): Likewise.
(cast::write_reproducer): Likewise.
(call::write_reproducer): Likewise.
(call_through_ptr::write_reproducer): Likewise.
(array_access::write_reproducer): Likewise.
(access_field_of_lvalue::write_reproducer): Likewise.
(access_field_rvalue::write_reproducer): Likewise.
(dereference_field_rvalue::write_reproducer): Likewise.
(dereference_rvalue::write_reproducer): Likewise.
(get_address_of_lvalue::write_reproducer): Likewise.
(local::write_reproducer): Likewise.
(eval::write_reproducer): Likewise.
(assignment::write_reproducer): Likewise.
(assignment_op::write_reproducer): Likewise.
(comment::write_reproducer): Likewise.
(conditional::write_reproducer): Likewise.
(jump::write_reproducer): Likewise.
(return_::write_reproducer): Likewise.
* libgccjit++.h (gccjit::context::dump_reproducer_to_file): New.
* libgccjit.c (gcc_jit_context_new_location): Pass "true" as
param "created_by_user".
(gcc_jit_context_dump_reproducer_to_file): New API entrypoint.
* libgccjit.h (gcc_jit_context_dump_reproducer_to_file): New API
entrypoint.
* libgccjit.map (gcc_jit_context_dump_reproducer_to_file): New API
entrypoint.
2015-01-12 David Malcolm <dmalcolm@redhat.com>
* jit-recording.c (class gcc::jit::rvalue_usage_validator): New.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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;
......
2015-01-13 David Malcolm <dmalcolm@redhat.com>
* jit.dg/harness.h (set_up_logging): Move string concatenation
into...
(concat_strings): New function.
(dump_reproducer): New function.
(test_jit): Call dump_reproducer.
* jit.dg/jit.exp (is_testcase_meant_to_generate_a_reproducer): New
function.
(jit-dg-test): Delete any generated reproducer from previous runs.
Verify that a generated reproducer was created, and verify that it
compiles.
* jit.dg/test-nested-contexts.c (main): Call
gcc_jit_context_dump_reproducer_to_file.
2015-01-13 H.J. Lu <hongjiu.lu@intel.com>
* gcc.dg/aru-2.c: Add dg-require-profiling.
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment