blob: 117ff70114ca34a846448b8c5d2ec0a2db150e3b [file] [log] [blame]
/* Internals of libgccjit: classes for recording calls made to the JIT API.
Copyright (C) 2013-2021 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "pretty-print.h"
#include "toplev.h"
#include <pthread.h>
#include "jit-builtins.h"
#include "jit-recording.h"
#include "jit-playback.h"
namespace gcc {
namespace jit {
// class dump
dump::dump (recording::context &ctxt,
const char *filename,
bool update_locations)
: m_ctxt (ctxt),
m_filename (filename),
m_update_locations (update_locations),
m_line (0),
m_column (0)
{
m_file = fopen (filename, "w");
if (!m_file)
ctxt.add_error (NULL,
"error opening dump file %s for writing: %s",
filename,
xstrerror (errno));
}
dump::~dump ()
{
if (m_file)
{
int err = fclose (m_file);
if (err)
m_ctxt.add_error (NULL,
"error closing dump file %s: %s",
m_filename,
xstrerror (errno));
}
}
/* Write the given message to the dump, using printf-formatting
conventions, updating the line/column within the dump.
Emit an error on the context if a failure occurs. */
void
dump::write (const char *fmt, ...)
{
int len;
va_list ap;
char *buf;
/* If there was an error opening the file, we've already reported it.
Don't attempt further work. */
if (!m_file)
return;
va_start (ap, fmt);
len = vasprintf (&buf, fmt, ap);
va_end (ap);
if (buf == NULL || len < 0)
{
m_ctxt.add_error (NULL, "malloc failure writing to dumpfile %s",
m_filename);
return;
}
if (fwrite (buf, strlen (buf), 1, m_file) != 1)
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++)
{
if ('\n' == *ptr)
{
m_line++;
m_column = 0;
}
else
m_column++;
}
free (buf);
}
/* Construct a gcc::jit::recording::location instance for the current
location within the dump. */
recording::location *
dump::make_location () const
{
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:
const char * ensure_identifier_is_unique (const char *candidate, void *ptr);
private:
hash_map<recording::memento *, const char *> m_map_memento_to_identifier;
struct hash_traits : public string_hash
{
static void remove (const char *) {}
};
hash_set<const char *, false, hash_traits> m_set_identifiers;
allocator m_allocator;
};
/* gcc::jit::reproducer's constructor. */
reproducer::reproducer (recording::context &ctxt,
const char *filename) :
dump (ctxt, filename, 0),
m_map_memento_to_identifier (),
m_set_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"
" ");
}
}
/* Ensure that STR is a valid C identifier by overwriting
any invalid chars in-place with underscores.
This doesn't special-case the first character. */
static void
convert_to_identifier (char *str)
{
for (char *p = str; *p; p++)
if (!ISALNUM (*p))
*p = '_';
}
/* Given CANDIDATE, a possible C identifier for use in a reproducer,
ensure that it is unique within the generated source file by
appending PTR to it if necessary. Return the resulting string.
The reproducer will eventually clean up the buffer in its dtor. */
const char *
reproducer::ensure_identifier_is_unique (const char *candidate, void *ptr)
{
if (m_set_identifiers.contains (candidate))
candidate = m_allocator.xstrdup_printf ("%s_%p", candidate, ptr);
gcc_assert (!m_set_identifiers.contains (candidate));
m_set_identifiers.add (candidate);
return candidate;
}
/* 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)
{
const char *result;
if (strlen (m->get_debug_string ()) < 100)
{
char *buf = m_allocator.xstrdup_printf ("%s_%s",
prefix,
m->get_debug_string ());
convert_to_identifier (buf);
result = buf;
}
else
result = m_allocator.xstrdup_printf ("%s_%p",
prefix, (void *) m);
result = ensure_identifier_is_unique (result, m);
m_map_memento_to_identifier.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_map_memento_to_identifier.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;
}
/* A helper class for implementing make_debug_string, for building
a temporary string from a vec of rvalues. */
class comma_separated_string
{
public:
comma_separated_string (const auto_vec<recording::rvalue *> &rvalues,
enum recording::precedence prec);
~comma_separated_string ();
const char *as_char_ptr () const { return m_buf; }
private:
char *m_buf;
};
/* comma_separated_string's ctor
Build m_buf. */
comma_separated_string::comma_separated_string
(const auto_vec<recording::rvalue *> &rvalues,
enum recording::precedence prec)
: m_buf (NULL)
{
/* Calculate length of said buffer. */
size_t sz = 1; /* nil terminator */
for (unsigned i = 0; i< rvalues.length (); i++)
{
sz += strlen (rvalues[i]->get_debug_string_parens (prec));
sz += 2; /* ", " separator */
}
/* Now allocate and populate the buffer. */
m_buf = new char[sz];
size_t len = 0;
for (unsigned i = 0; i< rvalues.length (); i++)
{
strcpy (m_buf + len, rvalues[i]->get_debug_string_parens (prec));
len += strlen (rvalues[i]->get_debug_string_parens (prec));
if (i + 1 < rvalues.length ())
{
strcpy (m_buf + len, ", ");
len += 2;
}
}
m_buf[len] = '\0';
}
/* comma_separated_string's dtor. */
comma_separated_string::~comma_separated_string ()
{
delete[] m_buf;
}
/**********************************************************************
Recording.
**********************************************************************/
/* Get the playback::location for the given recording::location,
handling a NULL input with a NULL output. */
playback::location *
recording::playback_location (replayer *r, recording::location *loc)
{
if (loc)
return loc->playback_location (r);
else
return NULL;
}
/* Get a const char * for the given recording::string
handling a NULL input with a NULL output. */
const char *
recording::playback_string (recording::string *str)
{
if (str)
return str->c_str ();
else
return NULL;
}
/* Get the playback::block for the given recording::block,
handling a NULL input with a NULL output. */
playback::block *
recording::playback_block (recording::block *b)
{
if (b)
return b->playback_block ();
else
return NULL;
}
/* Methods of cc::jit::recording::context. */
/* The constructor for gcc::jit::recording::context, used by
gcc_jit_context_acquire and gcc_jit_context_new_child_context. */
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_timer (NULL),
m_error_count (0),
m_first_error_str (NULL),
m_owns_first_error_str (false),
m_last_error_str (NULL),
m_owns_last_error_str (false),
m_mementos (),
m_compound_types (),
m_globals (),
m_functions (),
m_FILE_type (NULL),
m_builtins_manager(NULL)
{
if (parent_ctxt)
{
/* Inherit options from parent. */
for (unsigned i = 0;
i < sizeof (m_str_options) / sizeof (m_str_options[0]);
i++)
{
const char *parent_opt = parent_ctxt->m_str_options[i];
m_str_options[i] = parent_opt ? xstrdup (parent_opt) : NULL;
}
memcpy (m_int_options,
parent_ctxt->m_int_options,
sizeof (m_int_options));
memcpy (m_bool_options,
parent_ctxt->m_bool_options,
sizeof (m_bool_options));
memcpy (m_inner_bool_options,
parent_ctxt->m_inner_bool_options,
sizeof (m_inner_bool_options));
set_logger (parent_ctxt->get_logger ());
}
else
{
memset (m_str_options, 0, sizeof (m_str_options));
memset (m_int_options, 0, sizeof (m_int_options));
memset (m_bool_options, 0, sizeof (m_bool_options));
memset (m_inner_bool_options, 0, sizeof (m_inner_bool_options));
}
memset (m_basic_types, 0, sizeof (m_basic_types));
}
/* The destructor for gcc::jit::recording::context, implicitly used by
gcc_jit_context_release. */
recording::context::~context ()
{
JIT_LOG_SCOPE (get_logger ());
int i;
memento *m;
FOR_EACH_VEC_ELT (m_mementos, i, m)
{
delete m;
}
for (i = 0; i < GCC_JIT_NUM_STR_OPTIONS; ++i)
free (m_str_options[i]);
char *optname;
FOR_EACH_VEC_ELT (m_command_line_options, i, optname)
free (optname);
FOR_EACH_VEC_ELT (m_driver_options, i, optname)
free (optname);
if (m_builtins_manager)
delete m_builtins_manager;
if (m_owns_first_error_str)
free (m_first_error_str);
if (m_owns_last_error_str)
if (m_last_error_str != m_first_error_str)
free (m_last_error_str);
}
/* Add the given mememto to the list of those tracked by this
gcc::jit::recording::context, so that e.g. it can be deleted
when this context is released. */
void
recording::context::record (memento *m)
{
gcc_assert (m);
m_mementos.safe_push (m);
}
/* Replay this context (and any parents) into the given replayer. */
void
recording::context::replay_into (replayer *r)
{
JIT_LOG_SCOPE (get_logger ());
int i;
memento *m;
/* If we have a parent context, we must replay it. This will
recursively walk backwards up the historical tree, then replay things
forwards "in historical order", starting with the ultimate parent
context, until we reach the "this" context.
Note that we fully replay the parent, then fully replay the child,
which means that inter-context references can only exist from child
to parent, not the other way around.
All of this replaying is suboptimal - it would be better to do the
work for the parent context *once*, rather than replaying the parent
every time we replay each child. However, fixing this requires deep
surgery to lifetime-management: we'd need every context family tree
to have its own GC heap, and to initialize the GCC code to use that
heap (with a mutex on such a heap). */
if (m_parent_ctxt)
m_parent_ctxt->replay_into (r);
if (r->errors_occurred ())
return;
/* Replay this context's saved operations into r. */
FOR_EACH_VEC_ELT (m_mementos, i, m)
{
/* Disabled low-level debugging, here if we need it: print what
we're replaying.
Note that the calls to get_debug_string might lead to more
mementos being created for the strings.
This can also be used to exercise the debug_string
machinery. */
if (0)
printf ("context %p replaying (%p): %s\n",
(void *)this, (void *)m, m->get_debug_string ());
m->replay_into (r);
if (r->errors_occurred ())
return;
}
}
/* During a playback, we associate objects from the recording with
their counterparts during this playback.
For simplicity, we store this within the recording objects.
The following method cleans away these associations, to ensure that
we never have out-of-date associations lingering on subsequent
playbacks (the objects pointed to are GC-managed, but the
recording objects don't own refs to them). */
void
recording::context::disassociate_from_playback ()
{
JIT_LOG_SCOPE (get_logger ());
int i;
memento *m;
if (m_parent_ctxt)
m_parent_ctxt->disassociate_from_playback ();
FOR_EACH_VEC_ELT (m_mementos, i, m)
{
m->set_playback_obj (NULL);
}
}
/* Create a recording::string instance and add it to this context's list
of mementos.
This creates a fresh copy of the given 0-terminated buffer. */
recording::string *
recording::context::new_string (const char *text, bool escaped)
{
if (!text)
return NULL;
recording::string *result = new string (this, text, escaped);
record (result);
return result;
}
/* Create a recording::location instance and add it to this context's
list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_location. */
recording::location *
recording::context::new_location (const char *filename,
int line,
int column,
bool created_by_user)
{
recording::location *result =
new recording::location (this,
new_string (filename),
line, column,
created_by_user);
record (result);
return result;
}
/* If we haven't seen this enum value yet, create a recording::type
instance and add it to this context's list of mementos.
If we have seen it before, reuse our cached value, so that repeated
calls on the context give the same object.
If we have a parent context, the cache is within the ultimate
ancestor context.
Implements the post-error-checking part of
gcc_jit_context_get_type. */
recording::type *
recording::context::get_type (enum gcc_jit_types kind)
{
if (!m_basic_types[kind])
{
if (m_parent_ctxt)
m_basic_types[kind] = m_parent_ctxt->get_type (kind);
else
{
recording::type *result = new memento_of_get_type (this, kind);
record (result);
m_basic_types[kind] = result;
}
}
return m_basic_types[kind];
}
/* Get a recording::type instance for the given size and signedness.
This is implemented in terms of recording::context::get_type
above.
Implements the post-error-checking part of
gcc_jit_context_get_int_type. */
recording::type *
recording::context::get_int_type (int num_bytes, int is_signed)
{
/* We can't use a switch here since some of the values are macros affected
by options; e.g. i386.h has
#define LONG_TYPE_SIZE (TARGET_X32 ? 32 : BITS_PER_WORD)
Compare with tree.c's make_or_reuse_type. Note that the _SIZE macros
are in bits, rather than bytes.
*/
const int num_bits = num_bytes * 8;
if (num_bits == INT_TYPE_SIZE)
return get_type (is_signed
? GCC_JIT_TYPE_INT
: GCC_JIT_TYPE_UNSIGNED_INT);
if (num_bits == CHAR_TYPE_SIZE)
return get_type (is_signed
? GCC_JIT_TYPE_SIGNED_CHAR
: GCC_JIT_TYPE_UNSIGNED_CHAR);
if (num_bits == SHORT_TYPE_SIZE)
return get_type (is_signed
? GCC_JIT_TYPE_SHORT
: GCC_JIT_TYPE_UNSIGNED_SHORT);
if (num_bits == LONG_TYPE_SIZE)
return get_type (is_signed
? GCC_JIT_TYPE_LONG
: GCC_JIT_TYPE_UNSIGNED_LONG);
if (num_bits == LONG_LONG_TYPE_SIZE)
return get_type (is_signed
? GCC_JIT_TYPE_LONG_LONG
: GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
/* Some other size, not corresponding to the C int types. */
/* To be written: support arbitrary other sizes, sharing by
memoizing at the recording::context level? */
gcc_unreachable ();
}
/* Create a recording::type instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_array_type. */
recording::type *
recording::context::new_array_type (recording::location *loc,
recording::type *element_type,
int num_elements)
{
if (struct_ *s = element_type->dyn_cast_struct ())
if (!s->get_fields ())
{
add_error (NULL,
"cannot create an array of type %s"
" until the fields have been set",
s->get_name ()->c_str ());
return NULL;
}
recording::type *result =
new recording::array_type (this, loc, element_type, num_elements);
record (result);
return result;
}
/* Create a recording::field instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_field. */
recording::field *
recording::context::new_field (recording::location *loc,
recording::type *type,
const char *name)
{
recording::field *result =
new recording::field (this, loc, type, new_string (name));
record (result);
return result;
}
/* Create a recording::bitfield instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_bitfield. */
recording::field *
recording::context::new_bitfield (recording::location *loc,
recording::type *type,
int width,
const char *name)
{
recording::field *result =
new recording::bitfield (this, loc, type, width, new_string (name));
record (result);
return result;
}
/* Create a recording::struct_ instance and add it to this context's
list of mementos and list of compound types.
Implements the post-error-checking part of
gcc_jit_context_new_struct_type. */
recording::struct_ *
recording::context::new_struct_type (recording::location *loc,
const char *name)
{
recording::struct_ *result = new struct_ (this, loc, new_string (name));
record (result);
m_compound_types.safe_push (result);
return result;
}
/* Create a recording::union_ instance and add it to this context's
list of mementos and list of compound types.
Implements the first post-error-checking part of
gcc_jit_context_new_union_type. */
recording::union_ *
recording::context::new_union_type (recording::location *loc,
const char *name)
{
recording::union_ *result = new union_ (this, loc, new_string (name));
record (result);
m_compound_types.safe_push (result);
return result;
}
/* Create a recording::function_type instance and add it to this context's
list of mementos.
Used by new_function_ptr_type and by builtins_manager::make_fn_type. */
recording::function_type *
recording::context::new_function_type (recording::type *return_type,
int num_params,
recording::type **param_types,
int is_variadic)
{
recording::function_type *fn_type
= new function_type (this,
return_type,
num_params,
param_types,
is_variadic);
record (fn_type);
return fn_type;
}
/* Create a recording::type instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_function_ptr_type. */
recording::type *
recording::context::new_function_ptr_type (recording::location *, /* unused loc */
recording::type *return_type,
int num_params,
recording::type **param_types,
int is_variadic)
{
recording::function_type *fn_type
= new_function_type (return_type,
num_params,
param_types,
is_variadic);
/* Return a pointer-type to the function type. */
return fn_type->get_pointer ();
}
/* Create a recording::param instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_param. */
recording::param *
recording::context::new_param (recording::location *loc,
recording::type *type,
const char *name)
{
recording::param *result = new recording::param (this, loc, type, new_string (name));
record (result);
return result;
}
/* Create a recording::function instance and add it to this context's list
of mementos and list of functions.
Implements the post-error-checking part of
gcc_jit_context_new_function. */
recording::function *
recording::context::new_function (recording::location *loc,
enum gcc_jit_function_kind kind,
recording::type *return_type,
const char *name,
int num_params,
recording::param **params,
int is_variadic,
enum built_in_function builtin_id)
{
recording::function *result =
new recording::function (this,
loc, kind, return_type,
new_string (name),
num_params, params, is_variadic,
builtin_id);
record (result);
m_functions.safe_push (result);
return result;
}
/* Locate the builtins_manager (if any) for this family of contexts,
creating it if it doesn't exist already.
All of the recording contexts in a family share one builtins_manager:
if we have a child context, follow the parent links to get the
ultimate ancestor context, and look for it/store it there. */
builtins_manager *
recording::context::get_builtins_manager ()
{
if (m_parent_ctxt)
return m_parent_ctxt->get_builtins_manager ();
if (!m_builtins_manager)
m_builtins_manager = new builtins_manager (this);
return m_builtins_manager;
}
/* Get a recording::function instance, which is lazily-created and added
to the context's lists of mementos.
Implements the post-error-checking part of
gcc_jit_context_get_builtin_function. */
recording::function *
recording::context::get_builtin_function (const char *name)
{
builtins_manager *bm = get_builtins_manager ();
return bm->get_builtin_function (name);
}
/* Create a recording::global instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_global. */
recording::lvalue *
recording::context::new_global (recording::location *loc,
enum gcc_jit_global_kind kind,
recording::type *type,
const char *name)
{
recording::global *result =
new recording::global (this, loc, kind, type, new_string (name));
record (result);
m_globals.safe_push (result);
return result;
}
/* Create a recording::memento_of_new_string_literal instance and add it
to this context's list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_string_literal. */
recording::rvalue *
recording::context::new_string_literal (const char *value)
{
recording::rvalue *result =
new memento_of_new_string_literal (this, NULL, new_string (value));
record (result);
return result;
}
/* Create a recording::memento_of_new_rvalue_from_vector instance and add it
to this context's list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_rvalue_from_vector. */
recording::rvalue *
recording::context::new_rvalue_from_vector (location *loc,
vector_type *type,
rvalue **elements)
{
recording::rvalue *result
= new memento_of_new_rvalue_from_vector (this, loc, type, elements);
record (result);
return result;
}
/* Create a recording::unary_op instance and add it to this context's
list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_unary_op. */
recording::rvalue *
recording::context::new_unary_op (recording::location *loc,
enum gcc_jit_unary_op op,
recording::type *result_type,
recording::rvalue *a)
{
recording::rvalue *result =
new unary_op (this, loc, op, result_type, a);
record (result);
return result;
}
/* Create a recording::binary_op instance and add it to this context's
list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_binary_op. */
recording::rvalue *
recording::context::new_binary_op (recording::location *loc,
enum gcc_jit_binary_op op,
recording::type *result_type,
recording::rvalue *a,
recording::rvalue *b)
{
recording::rvalue *result =
new binary_op (this, loc, op, result_type, a, b);
record (result);
return result;
}
/* Create a recording::comparison instance and add it to this context's
list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_comparison. */
recording::rvalue *
recording::context::new_comparison (recording::location *loc,
enum gcc_jit_comparison op,
recording::rvalue *a,
recording::rvalue *b)
{
recording::rvalue *result = new comparison (this, loc, op, a, b);
record (result);
return result;
}
/* Create a recording::cast instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_cast. */
recording::rvalue *
recording::context::new_cast (recording::location *loc,
recording::rvalue *expr,
recording::type *type_)
{
recording::rvalue *result = new cast (this, loc, expr, type_);
record (result);
return result;
}
/* Create a recording::call instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_call. */
recording::rvalue *
recording::context::new_call (recording::location *loc,
function *func,
int numargs , recording::rvalue **args)
{
recording::rvalue *result = new call (this, loc, func, numargs, args);
record (result);
return result;
}
/* Create a recording::call_through_ptr instance and add it to this
context's list of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_call_through_ptr. */
recording::rvalue *
recording::context::new_call_through_ptr (recording::location *loc,
recording::rvalue *fn_ptr,
int numargs,
recording::rvalue **args)
{
recording::rvalue *result = new call_through_ptr (this, loc, fn_ptr, numargs, args);
record (result);
return result;
}
/* Create a recording::array_access instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_array_access. */
recording::lvalue *
recording::context::new_array_access (recording::location *loc,
recording::rvalue *ptr,
recording::rvalue *index)
{
recording::lvalue *result = new array_access (this, loc, ptr, index);
record (result);
return result;
}
/* Create a recording::case_ instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_case. */
recording::case_ *
recording::context::new_case (recording::rvalue *min_value,
recording::rvalue *max_value,
recording::block *block)
{
recording::case_ *result = new case_ (this, min_value, max_value, block);
record (result);
return result;
}
/* Set the given string option for this context, or add an error if
it's not recognized.
Implements the post-error-checking part of
gcc_jit_context_set_str_option. */
void
recording::context::set_str_option (enum gcc_jit_str_option opt,
const char *value)
{
if (opt < 0 || opt >= GCC_JIT_NUM_STR_OPTIONS)
{
add_error (NULL,
"unrecognized (enum gcc_jit_str_option) value: %i", opt);
return;
}
free (m_str_options[opt]);
m_str_options[opt] = value ? xstrdup (value) : NULL;
log_str_option (opt);
}
/* Set the given integer option for this context, or add an error if
it's not recognized.
Implements the post-error-checking part of
gcc_jit_context_set_int_option. */
void
recording::context::set_int_option (enum gcc_jit_int_option opt,
int value)
{
if (opt < 0 || opt >= GCC_JIT_NUM_INT_OPTIONS)
{
add_error (NULL,
"unrecognized (enum gcc_jit_int_option) value: %i", opt);
return;
}
m_int_options[opt] = value;
log_int_option (opt);
}
/* Set the given boolean option for this context, or add an error if
it's not recognized.
Implements the post-error-checking part of
gcc_jit_context_set_bool_option. */
void
recording::context::set_bool_option (enum gcc_jit_bool_option opt,
int value)
{
if (opt < 0 || opt >= GCC_JIT_NUM_BOOL_OPTIONS)
{
add_error (NULL,
"unrecognized (enum gcc_jit_bool_option) value: %i", opt);
return;
}
m_bool_options[opt] = value ? true : false;
log_bool_option (opt);
}
void
recording::context::set_inner_bool_option (enum inner_bool_option inner_opt,
int value)
{
gcc_assert (inner_opt >= 0 && inner_opt < NUM_INNER_BOOL_OPTIONS);
m_inner_bool_options[inner_opt] = value ? true : false;
log_inner_bool_option (inner_opt);
}
/* Add the given optname to this context's list of extra options.
Implements the post-error-checking part of
gcc_jit_context_add_command_line_option. */
void
recording::context::add_command_line_option (const char *optname)
{
m_command_line_options.safe_push (xstrdup (optname));
}
/* Add any user-provided extra options, starting with any from
parent contexts.
Called by playback::context::make_fake_args. */
void
recording::context::append_command_line_options (vec <char *> *argvec)
{
if (m_parent_ctxt)
m_parent_ctxt->append_command_line_options (argvec);
int i;
char *optname;
FOR_EACH_VEC_ELT (m_command_line_options, i, optname)
argvec->safe_push (xstrdup (optname));
}
/* Add the given optname to this context's list of extra driver options. */
void
recording::context::add_driver_option (const char *optname)
{
m_driver_options.safe_push (xstrdup (optname));
}
/* Add any user-provided driver options, starting with any from
parent contexts.
Called by playback::context::invoke_driver. */
void
recording::context::append_driver_options (auto_string_vec *argvec)
{
if (m_parent_ctxt)
m_parent_ctxt->append_driver_options (argvec);
int i;
char *optname;
FOR_EACH_VEC_ELT (m_driver_options, i, optname)
argvec->safe_push (xstrdup (optname));
}
/* Add the given dumpname/out_ptr pair to this context's list of requested
dumps.
Implements the post-error-checking part of
gcc_jit_context_enable_dump. */
void
recording::context::enable_dump (const char *dumpname,
char **out_ptr)
{
requested_dump d;
gcc_assert (dumpname);
gcc_assert (out_ptr);
d.m_dumpname = dumpname;
d.m_out_ptr = out_ptr;
*out_ptr = NULL;
m_requested_dumps.safe_push (d);
}
/* Validate this context, and if it passes, compile it to memory
(within a mutex).
Implements the post-error-checking part of
gcc_jit_context_compile. */
result *
recording::context::compile ()
{
JIT_LOG_SCOPE (get_logger ());
log_all_options ();
validate ();
if (errors_occurred ())
return NULL;
/* Set up a compile_to_memory playback context. */
::gcc::jit::playback::compile_to_memory replayer (this);
/* Use it. */
replayer.compile ();
/* Get the jit::result (or NULL) from the
compile_to_memory playback context. */
return replayer.get_result_obj ();
}
/* Validate this context, and if it passes, compile it to a file
(within a mutex).
Implements the post-error-checking part of
gcc_jit_context_compile_to_file. */
void
recording::context::compile_to_file (enum gcc_jit_output_kind output_kind,
const char *output_path)
{
JIT_LOG_SCOPE (get_logger ());
log_all_options ();
validate ();
if (errors_occurred ())
return;
/* Set up a compile_to_file playback context. */
::gcc::jit::playback::compile_to_file replayer (this,
output_kind,
output_path);
/* Use it. */
replayer.compile ();
}
/* Format the given error using printf's conventions, print
it to stderr, and add it to the context. */
void
recording::context::add_error (location *loc, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
add_error_va (loc, fmt, ap);
va_end (ap);
}
/* Format the given error using printf's conventions, print
it to stderr, and add it to the context. */
void
recording::context::add_error_va (location *loc, const char *fmt, va_list ap)
{
int len;
char *malloced_msg;
const char *errmsg;
bool has_ownership;
JIT_LOG_SCOPE (get_logger ());
len = vasprintf (&malloced_msg, fmt, ap);
if (malloced_msg == NULL || len < 0)
{
errmsg = "out of memory generating error message";
has_ownership = false;
}
else
{
errmsg = malloced_msg;
has_ownership = true;
}
if (get_logger ())
get_logger ()->log ("error %i: %s", m_error_count, errmsg);
const char *ctxt_progname =
get_str_option (GCC_JIT_STR_OPTION_PROGNAME);
if (!ctxt_progname)
ctxt_progname = "libgccjit.so";
if (loc)
fprintf (stderr, "%s: %s: error: %s\n",
ctxt_progname,
loc->get_debug_string (),
errmsg);
else
fprintf (stderr, "%s: error: %s\n",
ctxt_progname,
errmsg);
if (!m_error_count)
{
m_first_error_str = const_cast <char *> (errmsg);
m_owns_first_error_str = has_ownership;
}
if (m_owns_last_error_str)
if (m_last_error_str != m_first_error_str)
free (m_last_error_str);
m_last_error_str = const_cast <char *> (errmsg);
m_owns_last_error_str = has_ownership;
m_error_count++;
}
/* Get the message for the first error that occurred on this context, or
NULL if no errors have occurred on it.
Implements the post-error-checking part of
gcc_jit_context_get_first_error. */
const char *
recording::context::get_first_error () const
{
return m_first_error_str;
}
/* Get the message for the last error that occurred on this context, or
NULL if no errors have occurred on it.
Implements the post-error-checking part of
gcc_jit_context_get_last_error. */
const char *
recording::context::get_last_error () const
{
return m_last_error_str;
}
/* Lazily generate and record a recording::type representing an opaque
struct named "FILE".
For use if client code tries to dereference the result of
get_type (GCC_JIT_TYPE_FILE_PTR). */
recording::type *
recording::context::get_opaque_FILE_type ()
{
if (!m_FILE_type)
m_FILE_type = new_struct_type (NULL, "FILE");
return m_FILE_type;
}
/* Dump a C-like representation of the given context to the given path.
If UPDATE_LOCATIONS is true, update the locations within the
context's mementos to point to the dumpfile.
Implements the post-error-checking part of
gcc_jit_context_dump_to_file. */
void
recording::context::dump_to_file (const char *path, bool update_locations)
{
int i;
dump d (*this, path, update_locations);
/* Forward declaration of structs and unions. */
compound_type *st;
FOR_EACH_VEC_ELT (m_compound_types, i, st)
{
d.write ("%s;\n\n", st->get_debug_string ());
}
/* Content of structs, where set. */
FOR_EACH_VEC_ELT (m_compound_types, i, st)
if (st->get_fields ())
{
st->get_fields ()->write_to_dump (d);
d.write ("\n");
}
/* Globals. */
global *g;
FOR_EACH_VEC_ELT (m_globals, i, g)
{
g->write_to_dump (d);
}
if (!m_globals.is_empty ())
d.write ("\n");
function *fn;
FOR_EACH_VEC_ELT (m_functions, i, fn)
{
fn->write_to_dump (d);
}
top_level_asm *tla;
FOR_EACH_VEC_ELT (m_top_level_asms, i, tla)
tla->write_to_dump (d);
}
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"
};
static const char * const
inner_bool_option_reproducer_strings[NUM_INNER_BOOL_OPTIONS] = {
"gcc_jit_context_set_bool_allow_unreachable_blocks",
"gcc_jit_context_set_bool_use_external_driver"
};
/* Write the current value of all options to the log file (if any). */
void
recording::context::log_all_options () const
{
int opt_idx;
if (!get_logger ())
return;
for (opt_idx = 0; opt_idx < GCC_JIT_NUM_STR_OPTIONS; opt_idx++)
log_str_option ((enum gcc_jit_str_option)opt_idx);
for (opt_idx = 0; opt_idx < GCC_JIT_NUM_INT_OPTIONS; opt_idx++)
log_int_option ((enum gcc_jit_int_option)opt_idx);
for (opt_idx = 0; opt_idx < GCC_JIT_NUM_BOOL_OPTIONS; opt_idx++)
log_bool_option ((enum gcc_jit_bool_option)opt_idx);
for (opt_idx = 0; opt_idx < NUM_INNER_BOOL_OPTIONS; opt_idx++)
log_inner_bool_option ((enum inner_bool_option)opt_idx);
}
/* Write the current value of the given string option to the
log file (if any). */
void
recording::context::log_str_option (enum gcc_jit_str_option opt) const
{
gcc_assert (opt < GCC_JIT_NUM_STR_OPTIONS);
if (get_logger ())
{
if (m_str_options[opt])
log ("%s: \"%s\"",
str_option_reproducer_strings[opt],
m_str_options[opt]);
else
log ("%s: NULL",
str_option_reproducer_strings[opt]);
}
}
/* Write the current value of the given int option to the
log file (if any). */
void
recording::context::log_int_option (enum gcc_jit_int_option opt) const
{
gcc_assert (opt < GCC_JIT_NUM_INT_OPTIONS);
if (get_logger ())
log ("%s: %i",
int_option_reproducer_strings[opt],
m_int_options[opt]);
}
/* Write the current value of the given bool option to the
log file (if any). */
void
recording::context::log_bool_option (enum gcc_jit_bool_option opt) const
{
gcc_assert (opt < GCC_JIT_NUM_BOOL_OPTIONS);
if (get_logger ())
log ("%s: %s",
bool_option_reproducer_strings[opt],
m_bool_options[opt] ? "true" : "false");
}
/* Write the current value of the given "inner" bool option to the
log file (if any). */
void
recording::context::log_inner_bool_option (enum inner_bool_option opt) const
{
gcc_assert (opt < NUM_INNER_BOOL_OPTIONS);
if (get_logger ())
log ("%s: %s",
inner_bool_option_reproducer_strings[opt],
m_inner_bool_options[opt] ? "true" : "false");
}
/* 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");
print_version (r.get_file (), " ", false);
r.write ("*/\n");
r.write ("#include <libgccjit.h>\n\n");
r.write ("#pragma GCC diagnostic ignored \"-Wunused-variable\"\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",
r.get_identifier (contexts[ctxt_idx]),
str_option_reproducer_strings[opt_idx]);
if (m_str_options[opt_idx])
r.write (" \"%s\");\n",
m_str_options[opt_idx]);
else
r.write (" NULL);\n");
}
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]);
for (int opt_idx = 0; opt_idx < NUM_INNER_BOOL_OPTIONS; opt_idx++)
r.write (" %s (%s, %i);\n",
inner_bool_option_reproducer_strings[opt_idx],
r.get_identifier (contexts[ctxt_idx]),
m_inner_bool_options[opt_idx]);
if (!m_command_line_options.is_empty ())
{
int i;
char *optname;
r.write (" /* User-provided command-line options. */\n");
FOR_EACH_VEC_ELT (m_command_line_options, i, optname)
r.write (" gcc_jit_context_add_command_line_option (%s, \"%s\");\n",
r.get_identifier (contexts[ctxt_idx]),
optname);
}
if (!m_driver_options.is_empty ())
{
int i;
char *optname;
r.write (" /* User-provided driver options. */\n");
FOR_EACH_VEC_ELT (m_driver_options, i, optname)
r.write (" gcc_jit_context_add_driver_option (%s, \"%s\");\n",
r.get_identifier (contexts[ctxt_idx]),
optname);
}
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. */
void
recording::context::get_all_requested_dumps (vec <recording::requested_dump> *out)
{
if (m_parent_ctxt)
m_parent_ctxt->get_all_requested_dumps (out);
out->reserve (m_requested_dumps.length ());
out->splice (m_requested_dumps);
}
/* Create a recording::top_level_asm instance and add it to this
context's list of mementos and to m_top_level_asms.
Implements the post-error-checking part of
gcc_jit_context_add_top_level_asm. */
void
recording::context::add_top_level_asm (recording::location *loc,
const char *asm_stmts)
{
recording::top_level_asm *asm_obj
= new recording::top_level_asm (this, loc, new_string (asm_stmts));
record (asm_obj);
m_top_level_asms.safe_push (asm_obj);
}
/* This is a pre-compilation check for the context (and any parents).
Detect errors within the context, adding errors if any are found. */
void
recording::context::validate ()
{
JIT_LOG_SCOPE (get_logger ());
if (m_parent_ctxt)
m_parent_ctxt->validate ();
int i;
function *fn;
FOR_EACH_VEC_ELT (m_functions, i, fn)
fn->validate ();
}
/* The implementation of class gcc::jit::recording::memento. */
/* Get a (const char *) debug description of the given memento, by
calling the pure-virtual make_debug_string hook, caching the
result.
It is intended that this should only be called in debugging and
error-handling paths, so this doesn't need to be particularly
optimized. */
const char *
recording::memento::get_debug_string ()
{
if (!m_debug_string)
m_debug_string = make_debug_string ();
return m_debug_string->c_str ();
}
/* Default implementation of recording::memento::write_to_dump, writing
an indented form of the memento's debug string to the dump. */
void
recording::memento::write_to_dump (dump &d)
{
d.write(" %s\n", get_debug_string ());
}
/* The implementation of class gcc::jit::recording::string. */
/* Constructor for gcc::jit::recording::string::string, allocating a
copy of the given text using new char[]. */
recording::string::string (context *ctxt, const char *text, bool escaped)
: memento (ctxt),
m_escaped (escaped)
{
m_len = strlen (text);
m_buffer = new char[m_len + 1];
strcpy (m_buffer, text);
}
/* Destructor for gcc::jit::recording::string::string. */
recording::string::~string ()
{
delete[] m_buffer;
}
/* Function for making gcc::jit::recording::string instances on a
context via printf-style formatting.
It is intended that this should only be called in debugging and
error-handling paths, so this doesn't need to be particularly
optimized, hence the double-copy of the string is acceptable. */
recording::string *
recording::string::from_printf (context *ctxt, const char *fmt, ...)
{
int len;
va_list ap;
char *buf;
recording::string *result;
va_start (ap, fmt);
len = vasprintf (&buf, fmt, ap);
va_end (ap);
if (buf == NULL || len < 0)
{
ctxt->add_error (NULL, "malloc failure");
return NULL;
}
result = ctxt->new_string (buf);
free (buf);
return result;
}
/* Implementation of recording::memento::make_debug_string for strings,
wrapping the given string in quotes and escaping as necessary. */
recording::string *
recording::string::make_debug_string ()
{
/* Avoid infinite recursion into strings when logging all mementos:
don't re-escape strings: */
if (m_escaped)
return this;
/* Wrap in quotes and do escaping etc */
size_t sz = (1 /* opening quote */
+ (m_len * 2) /* each char might get escaped */
+ 1 /* closing quote */
+ 1); /* nil termintator */
char *tmp = new char[sz];
size_t len = 0;
#define APPEND(CH) do { gcc_assert (len < sz); tmp[len++] = (CH); } while (0)
APPEND('"'); /* opening quote */
for (size_t i = 0; i < m_len ; i++)
{
char ch = m_buffer[i];
switch (ch)
{
default:
APPEND(ch);
break;
case '\t':
APPEND('\\');
APPEND('t');
break;
case '\n':
APPEND('\\');
APPEND('n');
break;
case '\\':
case '"':
APPEND('\\');
APPEND(ch);
break;
}
}
APPEND('"'); /* closing quote */
#undef APPEND
tmp[len] = '\0'; /* nil termintator */
string *result = m_ctxt->new_string (tmp, true);
delete[] tmp;
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.
Create a new playback::location and store it into the
recording::location's m_playback_obj field. */
void
recording::location::replay_into (replayer *r)
{
m_playback_obj = r->new_location (this,
m_filename->c_str (),
m_line,
m_column);
}
/* Implementation of recording::memento::make_debug_string for locations,
turning them into the usual form:
FILENAME:LINE:COLUMN
like we do when emitting diagnostics. */
recording::string *
recording::location::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s:%i:%i",
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*.
If this doesn't already exist, generate a new memento_of_get_pointer
instance and add it to this type's context's list of mementos.
Otherwise, use the cached type.
Implements the post-error-checking part of
gcc_jit_type_get_pointer. */
recording::type *
recording::type::get_pointer ()
{
if (!m_pointer_to_this_type)
{
m_pointer_to_this_type = new memento_of_get_pointer (this);
m_ctxt->record (m_pointer_to_this_type);
}
return m_pointer_to_this_type;
}
/* Given a type T, get the type const T.
Implements the post-error-checking part of
gcc_jit_type_get_const. */
recording::type *
recording::type::get_const ()
{
recording::type *result = new memento_of_get_const (this);
m_ctxt->record (result);
return result;
}
/* Given a type T, get the type volatile T.
Implements the post-error-checking part of
gcc_jit_type_get_volatile. */
recording::type *
recording::type::get_volatile ()
{
recording::type *result = new memento_of_get_volatile (this);
m_ctxt->record (result);
return result;
}
/* Given a type, get an aligned version of the type.
Implements the post-error-checking part of
gcc_jit_type_get_aligned. */
recording::type *
recording::type::get_aligned (size_t alignment_in_bytes)
{
recording::type *result
= new memento_of_get_aligned (this, alignment_in_bytes);
m_ctxt->record (result);
return result;
}
/* Given a type, get a vector version of the type.
Implements the post-error-checking part of
gcc_jit_type_get_vector. */
recording::type *
recording::type::get_vector (size_t num_units)
{
recording::type *result
= new vector_type (this, num_units);
m_ctxt->record (result);
return result;
}
const char *
recording::type::access_as_type (reproducer &r)
{
return r.get_identifier (this);
}
/* Override of default implementation of
recording::type::get_size.
Return the size in bytes. This is in use for global
initialization. */
size_t
recording::memento_of_get_type::get_size ()
{
int size;
switch (m_kind)
{
case GCC_JIT_TYPE_VOID:
return 0;
case GCC_JIT_TYPE_BOOL:
case GCC_JIT_TYPE_CHAR:
case GCC_JIT_TYPE_SIGNED_CHAR:
case GCC_JIT_TYPE_UNSIGNED_CHAR:
return 1;
case GCC_JIT_TYPE_SHORT:
case GCC_JIT_TYPE_UNSIGNED_SHORT:
size = SHORT_TYPE_SIZE;
break;
case GCC_JIT_TYPE_INT:
case GCC_JIT_TYPE_UNSIGNED_INT:
size = INT_TYPE_SIZE;
break;
case GCC_JIT_TYPE_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG:
size = LONG_TYPE_SIZE;
break;
case GCC_JIT_TYPE_LONG_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
size = LONG_LONG_TYPE_SIZE;
break;
case GCC_JIT_TYPE_FLOAT:
size = FLOAT_TYPE_SIZE;
break;
case GCC_JIT_TYPE_DOUBLE:
size = DOUBLE_TYPE_SIZE;
break;
case GCC_JIT_TYPE_LONG_DOUBLE:
size = LONG_DOUBLE_TYPE_SIZE;
break;
default:
/* As this function is called by
'gcc_jit_global_set_initializer' and
'recording::global::write_reproducer' possible types are only
integrals and are covered by the previous cases. */
gcc_unreachable ();
}
return size / BITS_PER_UNIT;
}
/* Implementation of pure virtual hook recording::type::dereference for
recording::memento_of_get_type. */
recording::type *
recording::memento_of_get_type::dereference ()
{
switch (m_kind)
{
default: gcc_unreachable ();
case GCC_JIT_TYPE_VOID:
return NULL;
case GCC_JIT_TYPE_VOID_PTR:
return m_ctxt->get_type (GCC_JIT_TYPE_VOID);
case GCC_JIT_TYPE_BOOL:
case GCC_JIT_TYPE_CHAR:
case GCC_JIT_TYPE_SIGNED_CHAR:
case GCC_JIT_TYPE_UNSIGNED_CHAR:
case GCC_JIT_TYPE_SHORT:
case GCC_JIT_TYPE_UNSIGNED_SHORT:
case GCC_JIT_TYPE_INT:
case GCC_JIT_TYPE_UNSIGNED_INT:
case GCC_JIT_TYPE_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG:
case GCC_JIT_TYPE_LONG_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
case GCC_JIT_TYPE_FLOAT:
case GCC_JIT_TYPE_DOUBLE:
case GCC_JIT_TYPE_LONG_DOUBLE:
case GCC_JIT_TYPE_COMPLEX_FLOAT:
case GCC_JIT_TYPE_COMPLEX_DOUBLE:
case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
/* Not a pointer: */
return NULL;
case GCC_JIT_TYPE_CONST_CHAR_PTR:
return m_ctxt->get_type (GCC_JIT_TYPE_CHAR)->get_const ();
case GCC_JIT_TYPE_SIZE_T:
/* Not a pointer: */
return NULL;
case GCC_JIT_TYPE_FILE_PTR:
/* Give the client code back an opaque "struct FILE". */
return m_ctxt->get_opaque_FILE_type ();
}
}
/* Implementation of pure virtual hook recording::type::is_int for
recording::memento_of_get_type. */
bool
recording::memento_of_get_type::is_int () const
{
switch (m_kind)
{
default: gcc_unreachable ();
case GCC_JIT_TYPE_VOID:
return false;
case GCC_JIT_TYPE_VOID_PTR:
return false;
case GCC_JIT_TYPE_BOOL:
return false;
case GCC_JIT_TYPE_CHAR:
case GCC_JIT_TYPE_SIGNED_CHAR:
case GCC_JIT_TYPE_UNSIGNED_CHAR:
case GCC_JIT_TYPE_SHORT:
case GCC_JIT_TYPE_UNSIGNED_SHORT:
case GCC_JIT_TYPE_INT:
case GCC_JIT_TYPE_UNSIGNED_INT:
case GCC_JIT_TYPE_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG:
case GCC_JIT_TYPE_LONG_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
return true;
case GCC_JIT_TYPE_FLOAT:
case GCC_JIT_TYPE_DOUBLE:
case GCC_JIT_TYPE_LONG_DOUBLE:
return false;
case GCC_JIT_TYPE_CONST_CHAR_PTR:
return false;
case GCC_JIT_TYPE_SIZE_T:
return true;
case GCC_JIT_TYPE_FILE_PTR:
return false;
case GCC_JIT_TYPE_COMPLEX_FLOAT:
case GCC_JIT_TYPE_COMPLEX_DOUBLE:
case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
return false;
}
}
/* Implementation of pure virtual hook recording::type::is_float for
recording::memento_of_get_type. */
bool
recording::memento_of_get_type::is_float () const
{
switch (m_kind)
{
default: gcc_unreachable ();
case GCC_JIT_TYPE_VOID:
return false;
case GCC_JIT_TYPE_VOID_PTR:
return false;
case GCC_JIT_TYPE_BOOL:
return false;
case GCC_JIT_TYPE_CHAR:
case GCC_JIT_TYPE_SIGNED_CHAR:
case GCC_JIT_TYPE_UNSIGNED_CHAR:
case GCC_JIT_TYPE_SHORT:
case GCC_JIT_TYPE_UNSIGNED_SHORT:
case GCC_JIT_TYPE_INT:
case GCC_JIT_TYPE_UNSIGNED_INT:
case GCC_JIT_TYPE_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG:
case GCC_JIT_TYPE_LONG_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
return false;
case GCC_JIT_TYPE_FLOAT:
case GCC_JIT_TYPE_DOUBLE:
case GCC_JIT_TYPE_LONG_DOUBLE:
return true;
case GCC_JIT_TYPE_CONST_CHAR_PTR:
return false;
case GCC_JIT_TYPE_SIZE_T:
return false;
case GCC_JIT_TYPE_FILE_PTR:
return false;
case GCC_JIT_TYPE_COMPLEX_FLOAT:
case GCC_JIT_TYPE_COMPLEX_DOUBLE:
case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
return true;
}
}
/* Implementation of pure virtual hook recording::type::is_bool for
recording::memento_of_get_type. */
bool
recording::memento_of_get_type::is_bool () const
{
switch (m_kind)
{
default: gcc_unreachable ();
case GCC_JIT_TYPE_VOID:
return false;
case GCC_JIT_TYPE_VOID_PTR:
return false;
case GCC_JIT_TYPE_BOOL:
return true;
case GCC_JIT_TYPE_CHAR:
case GCC_JIT_TYPE_SIGNED_CHAR:
case GCC_JIT_TYPE_UNSIGNED_CHAR:
case GCC_JIT_TYPE_SHORT:
case GCC_JIT_TYPE_UNSIGNED_SHORT:
case GCC_JIT_TYPE_INT:
case GCC_JIT_TYPE_UNSIGNED_INT:
case GCC_JIT_TYPE_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG:
case GCC_JIT_TYPE_LONG_LONG:
case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
return false;
case GCC_JIT_TYPE_FLOAT:
case GCC_JIT_TYPE_DOUBLE:
case GCC_JIT_TYPE_LONG_DOUBLE:
return false;
case GCC_JIT_TYPE_CONST_CHAR_PTR:
return false;
case GCC_JIT_TYPE_SIZE_T:
return false;
case GCC_JIT_TYPE_FILE_PTR:
return false;
case GCC_JIT_TYPE_COMPLEX_FLOAT:
case GCC_JIT_TYPE_COMPLEX_DOUBLE:
case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
return false;
}
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::memento_of_get_type. */
void
recording::memento_of_get_type::replay_into (replayer *r)
{
set_playback_obj (r->get_type (m_kind));
}
/* The implementation of class gcc::jit::recording::memento_of_get_type. */
/* Descriptive strings for each of enum gcc_jit_types. */
static const char * const get_type_strings[] = {
"void", /* GCC_JIT_TYPE_VOID */
"void *", /* GCC_JIT_TYPE_VOID_PTR */
"bool", /* GCC_JIT_TYPE_BOOL */
"char", /* GCC_JIT_TYPE_CHAR */
"signed char", /* GCC_JIT_TYPE_SIGNED_CHAR */
"unsigned char", /* GCC_JIT_TYPE_UNSIGNED_CHAR */
"short", /* GCC_JIT_TYPE_SHORT */
"unsigned short", /* GCC_JIT_TYPE_UNSIGNED_SHORT */
"int", /* GCC_JIT_TYPE_INT */
"unsigned int", /* GCC_JIT_TYPE_UNSIGNED_INT */
"long", /* GCC_JIT_TYPE_LONG */
"unsigned long", /* GCC_JIT_TYPE_UNSIGNED_LONG, */
"long long", /* GCC_JIT_TYPE_LONG_LONG */
"unsigned long long", /* GCC_JIT_TYPE_UNSIGNED_LONG_LONG */
"float", /* GCC_JIT_TYPE_FLOAT */
"double", /* GCC_JIT_TYPE_DOUBLE */
"long double", /* GCC_JIT_TYPE_LONG_DOUBLE */
"const char *", /* GCC_JIT_TYPE_CONST_CHAR_PTR */
"size_t", /* GCC_JIT_TYPE_SIZE_T */
"FILE *", /* GCC_JIT_TYPE_FILE_PTR */
"complex float", /* GCC_JIT_TYPE_COMPLEX_FLOAT */
"complex double", /* GCC_JIT_TYPE_COMPLEX_DOUBLE */
"complex long double" /* GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE */
};
/* Implementation of recording::memento::make_debug_string for
results of get_type, using a simple table of type names. */
recording::string *
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
recording::type::get_size for get_pointer. */
size_t
recording::memento_of_get_pointer::get_size ()
{
return POINTER_SIZE / BITS_PER_UNIT;
}
/* Override of default implementation of
recording::type::accepts_writes_from for get_pointer.
Require a pointer type, and allowing writes to
(const T *) from a (T*), but not the other way around. */
bool
recording::memento_of_get_pointer::accepts_writes_from (type *rtype)
{
/* Must be a pointer type: */
type *rtype_points_to = rtype->is_pointer ();
if (!rtype_points_to)
return false;
/* It's OK to assign to a (const T *) from a (T *). */
return m_other_type->unqualified ()
->accepts_writes_from (rtype_points_to);
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::memento_of_get_pointer. */
void
recording::memento_of_get_pointer::replay_into (replayer *)
{
set_playback_obj (m_other_type->playback_type ()->get_pointer ());
}
/* Implementation of recording::memento::make_debug_string for
results of get_pointer, adding " *" to the underlying type,
with special-casing to handle function pointer types. */
recording::string *
recording::memento_of_get_pointer::make_debug_string ()
{
/* Special-case function pointer types, to put the "*" in parens between
the return type and the params (for one level of dereferencing, at
least). */
if (function_type *fn_type = m_other_type->dyn_cast_function_type ())
return fn_type->make_debug_string_with_ptr ();
return string::from_printf (m_ctxt,
"%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
for recording::memento_of_get_const. */
void
recording::memento_of_get_const::replay_into (replayer *)
{
set_playback_obj (m_other_type->playback_type ()->get_const ());
}
/* Implementation of recording::memento::make_debug_string for
results of get_const, prepending "const ". */
recording::string *
recording::memento_of_get_const::make_debug_string ()
{
return string::from_printf (m_ctxt,
"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
for recording::memento_of_get_volatile. */
void
recording::memento_of_get_volatile::replay_into (replayer *)
{
set_playback_obj (m_other_type->playback_type ()->get_volatile ());
}
/* Implementation of recording::memento::make_debug_string for
results of get_volatile, prepending "volatile ". */
recording::string *
recording::memento_of_get_volatile::make_debug_string ()
{
return string::from_printf (m_ctxt,
"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::memento_of_get_aligned. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::memento_of_get_aligned. */
void
recording::memento_of_get_aligned::replay_into (replayer *)
{
set_playback_obj
(m_other_type->playback_type ()->get_aligned (m_alignment_in_bytes));
}
/* Implementation of recording::memento::make_debug_string for
results of get_aligned. */
recording::string *
recording::memento_of_get_aligned::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s __attribute__((aligned(%zi)))",
m_other_type->get_debug_string (),
m_alignment_in_bytes);
}
/* Implementation of recording::memento::write_reproducer for aligned
types. */
void
recording::memento_of_get_aligned::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "type");
r.write (" gcc_jit_type *%s =\n"
" gcc_jit_type_get_aligned (%s, %zi);\n",
id,
r.get_identifier_as_type (m_other_type),
m_alignment_in_bytes);
}
/* The implementation of class gcc::jit::recording::vector_type. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::vector_type. */
void
recording::vector_type::replay_into (replayer *)
{
set_playback_obj
(m_other_type->playback_type ()->get_vector (m_num_units));
}
/* Implementation of recording::memento::make_debug_string for
results of get_vector. */
recording::string *
recording::vector_type::make_debug_string ()
{
return string::from_printf
(m_ctxt,
"%s __attribute__((vector_size(sizeof (%s) * %zi)))",
m_other_type->get_debug_string (),
m_other_type->get_debug_string (),
m_num_units);
}
/* Implementation of recording::memento::write_reproducer for vector types. */
void
recording::vector_type::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "type");
r.write (" gcc_jit_type *%s =\n"
" gcc_jit_type_get_vector (%s, %zi);\n",
id,
r.get_identifier_as_type (m_other_type),
m_num_units);
}
/* The implementation of class gcc::jit::recording::array_type */
/* Implementation of pure virtual hook recording::type::dereference for
recording::array_type. */
recording::type *
recording::array_type::dereference ()
{
return m_element_type;
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::array_type. */
void
recording::array_type::replay_into (replayer *r)
{
set_playback_obj (r->new_array_type (playback_location (r, m_loc),
m_element_type->playback_type (),
m_num_elements));
}
/* Implementation of recording::memento::make_debug_string for
results of new_array_type. */
recording::string *
recording::array_type::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s[%d]",
m_element_type->get_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. */
recording::function_type::function_type (context *ctxt,
type *return_type,
int num_params,
type **param_types,
int is_variadic)
: type (ctxt),
m_return_type (return_type),
m_param_types (),
m_is_variadic (is_variadic)
{
for (int i = 0; i< num_params; i++)
m_param_types.safe_push (param_types[i]);
}
/* Implementation of pure virtual hook recording::type::dereference for
recording::function_type. */
recording::type *
recording::function_type::dereference ()
{
return NULL;
}
/* Implementation of virtual hook recording::type::is_same_type_as for
recording::function_type.
We override this to avoid requiring identity of function pointer types,
so that if client code has obtained the same signature in
different ways (e.g. via gcc_jit_context_new_function_ptr_type
vs gcc_jit_function_get_address), the different function_type
instances are treated as compatible.
We can't use type::accepts_writes_from for this as we need a stronger
notion of "sameness": if we have a fn_ptr type that has args that are
themselves fn_ptr types, then those args still need to match exactly.
Alternatively, we could consolidate attempts to create identical
function_type instances so that pointer equality works, but that runs
into issues about the lifetimes of the cache (w.r.t. nested contexts). */
bool
recording::function_type::is_same_type_as (type *other)
{
gcc_assert (other);
function_type *other_fn_type = other->dyn_cast_function_type ();
if (!other_fn_type)
return false;
/* Everything must match. */
if (!m_return_type->is_same_type_as (other_fn_type->m_return_type))
return false;
if (m_param_types.length () != other_fn_type->m_param_types.length ())
return false;
unsigned i;
type *param_type;
FOR_EACH_VEC_ELT (m_param_types, i, param_type)
if (!param_type->is_same_type_as (other_fn_type->m_param_types[i]))
return false;
if (m_is_variadic != other_fn_type->m_is_variadic)
return false;
/* Passed all tests. */
return true;
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::function_type. */
void
recording::function_type::replay_into (replayer *r)
{
/* Convert m_param_types to a vec of playback type. */
auto_vec <playback::type *> param_types;
int i;
recording::type *type;
param_types.create (m_param_types.length ());
FOR_EACH_VEC_ELT (m_param_types, i, type)
param_types.safe_push (type->playback_type ());
set_playback_obj (r->new_function_type (m_return_type->playback_type (),
&param_types,
m_is_variadic));
}
/* Special-casing for make_debug_string for get_pointer results for
handling (one level) of pointers to functions. */
recording::string *
recording::function_type::make_debug_string_with_ptr ()
{
return make_debug_string_with ("(*) ");
}
/* Implementation of recording::memento::make_debug_string for
results of new_function_type. */
recording::string *
recording::function_type::make_debug_string ()
{
return make_debug_string_with ("");
}
/* Build a debug string representation of the form:
RESULT_TYPE INSERT (PARAM_TYPES)
for use when handling 0 and 1 level of indirection to this
function type. */
recording::string *
recording::function_type::make_debug_string_with (const char *insert)
{
/* First, build a buffer for the arguments. */
/* Calculate length of said buffer. */
size_t sz = 1; /* nil terminator */
for (unsigned i = 0; i< m_param_types.length (); i++)
{
sz += strlen (m_param_types[i]->get_debug_string ());
sz += 2; /* ", " separator */
}
if (m_is_variadic)
sz += 5; /* ", ..." separator and ellipsis */
/* Now allocate and populate the buffer. */
char *argbuf = new char[sz];
size_t len = 0;
for (unsigned i = 0; i< m_param_types.length (); i++)
{
strcpy (argbuf + len, m_param_types[i]->get_debug_string ());
len += strlen (m_param_types[i]->get_debug_string ());
if (i + 1 < m_param_types.length ())
{
strcpy (argbuf + len, ", ");
len += 2;
}
}
if (m_is_variadic)
{
if (m_param_types.length ())
{
strcpy (argbuf + len, ", ");
len += 2;
}
strcpy (argbuf + len, "...");
len += 3;
}
argbuf[len] = '\0';
/* ...and use it to get the string for the call as a whole. */
string *result = string::from_printf (m_ctxt,
"%s %s(%s)",
m_return_type->get_debug_string (),
insert,
argbuf);
delete[] argbuf;
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
for recording::field. */
void
recording::field::replay_into (replayer *r)
{
set_playback_obj (r->new_field (playback_location (r, m_loc),
m_type->playback_type (),
playback_string (m_name)));
}
/* Override the default implementation of
recording::memento::write_to_dump. Dump each field
by dumping a line of the form:
TYPE NAME;
so that we can build up a struct/union field by field. */
void
recording::field::write_to_dump (dump &d)
{
d.write (" %s %s;\n",
m_type->get_debug_string (),
m_name->c_str ());
}
/* Implementation of recording::memento::make_debug_string for
results of new_field. */
recording::string *
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::bitfield. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::bitfield. */
void
recording::bitfield::replay_into (replayer *r)
{
set_playback_obj (r->new_bitfield (playback_location (r, m_loc),
m_type->playback_type (),
m_width,
playback_string (m_name)));
}
/* Override the default implementation of
recording::memento::write_to_dump. Dump each bit field
by dumping a line of the form:
TYPE NAME:WIDTH;
so that we can build up a struct/union field by field. */
void
recording::bitfield::write_to_dump (dump &d)
{
d.write (" %s %s:%d;\n",
m_type->get_debug_string (),
m_name->c_str (),
m_width);
}
/* Implementation of recording::memento::make_debug_string for
results of new_bitfield. */
recording::string *
recording::bitfield::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s:%d",
m_name->c_str (), m_width);
}
/* Implementation of recording::memento::write_reproducer for bitfields. */
void
recording::bitfield::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "bitfield");
r.write (" gcc_jit_field *%s =\n"
" gcc_jit_context_new_bitfield (%s,\n"
" %s, /* gcc_jit_location *loc */\n"
" %s, /* gcc_jit_type *type, */\n"
" %d, /* int width, */\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_width,
m_name->get_debug_string ());
}
/* The implementation of class gcc::jit::recording::compound_type */
/* The constructor for gcc::jit::recording::compound_type. */
recording::compound_type::compound_type (context *ctxt,
location *loc,
string *name)
: type (ctxt),
m_loc (loc),
m_name (name),
m_fields (NULL)
{
}
/* Set the fields of a compound type.
Implements the post-error-checking part of
gcc_jit_struct_set_fields, and is also used by
gcc_jit_context_new_union_type. */
void
recording::compound_type::set_fields (location *loc,
int num_fields,
field **field_array)
{
m_loc = loc;
gcc_assert (m_fields == NULL);
m_fields = new fields (this, num_fields, field_array);
m_ctxt->record (m_fields);
}
/* Implementation of pure virtual hook recording::type::dereference for
recording::compound_type. */
recording::type *
recording::compound_type::dereference ()
{
return NULL; /* not a pointer */
}
/* The implementation of class gcc::jit::recording::struct_. */
/* The constructor for gcc::jit::recording::struct_. */
recording::struct_::struct_ (context *ctxt,
location *loc,
string *name)
: compound_type (ctxt, loc, name)
{
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::struct_. */
void
recording::struct_::replay_into (replayer *r)
{
set_playback_obj (
r->new_compound_type (playback_location (r, get_loc ()),
get_name ()->c_str (),
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. */
recording::string *
recording::struct_::make_debug_string ()
{
return string::from_printf (m_ctxt,
"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_. */
recording::union_::union_ (context *ctxt,
location *loc,
string *name)
: compound_type (ctxt, loc, name)
{
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::union_. */
void
recording::union_::replay_into (replayer *r)
{
set_playback_obj (
r->new_compound_type (playback_location (r, get_loc ()),
get_name ()->c_str (),
false /* is_struct */));
}
/* Implementation of recording::memento::make_debug_string for
unions. */
recording::string *
recording::union_::make_debug_string ()
{
return string::from_printf (m_ctxt,
"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. */
recording::fields::fields (compound_type *struct_or_union,
int num_fields,
field **fields)
: memento (struct_or_union->m_ctxt),
m_struct_or_union (struct_or_union),
m_fields ()
{
for (int i = 0; i < num_fields; i++)
{
gcc_assert (fields[i]->get_container () == NULL);
fields[i]->set_container (m_struct_or_union);
m_fields.safe_push (fields[i]);
}
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::fields. */
void
recording::fields::replay_into (replayer *)
{
auto_vec<playback::field *> playback_fields;
playback_fields.create (m_fields.length ());
for (unsigned i = 0; i < m_fields.length (); i++)
playback_fields.safe_push (m_fields[i]->playback_field ());
m_struct_or_union->playback_compound_type ()->set_fields (&playback_fields);
}
/* Override the default implementation of
recording::memento::write_to_dump by writing a union/struct
declaration of this form:
struct/union NAME {
TYPE_1 NAME_1;
TYPE_2 NAME_2;
....
TYPE_N NAME_N;
};
to the dump. */
void
recording::fields::write_to_dump (dump &d)
{
int i;
field *f;
d.write ("%s\n{\n", m_struct_or_union->get_debug_string ());
FOR_EACH_VEC_ELT (m_fields, i, f)
f->write_to_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 (m_struct_or_union->dyn_cast_struct () == NULL)
/* 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. */
recording::string *
recording::fields::make_debug_string ()
{
return string::from_printf (m_ctxt,
"fields");
}
/* The implementation of class gcc::jit::recording::rvalue. */
/* Create a recording::access_field_rvalue instance and add it to
the rvalue's context's list of mementos.
Implements the post-error-checking part of
gcc_jit_rvalue_access_field. */
recording::rvalue *
recording::rvalue::access_field (recording::location *loc,
field *field)
{
recording::rvalue *result =
new access_field_rvalue (m_ctxt, loc, this, field);
m_ctxt->record (result);
return result;
}
/* Create a recording::dereference_field_rvalue instance and add it to
the rvalue's context's list of mementos.
Implements the post-error-checking part of
gcc_jit_rvalue_dereference_field. */
recording::lvalue *
recording::rvalue::dereference_field (recording::location *loc,
field *field)
{
recording::lvalue *result =
new dereference_field_rvalue (m_ctxt, loc, this, field);
m_ctxt->record (result);
return result;
}
/* Create a recording::dereference_rvalue instance and add it to the
rvalue's context's list of mementos.
Implements the post-error-checking part of
gcc_jit_rvalue_dereference. */
recording::lvalue *
recording::rvalue::dereference (recording::location *loc)
{
recording::lvalue *result =
new dereference_rvalue (m_ctxt, loc, this);
m_ctxt->record (result);
return result;
}
/* An rvalue visitor, for validating that every rvalue within an expression
trees within "STMT" has the correct scope (e.g. no access to locals
of a different function). */
class rvalue_usage_validator : public recording::rvalue_visitor
{
public:
rvalue_usage_validator (const char *api_funcname,
recording::context *ctxt,
recording::statement *stmt);
void
visit (recording::rvalue *rvalue) FINAL OVERRIDE;
private:
const char *m_api_funcname;
recording::context *m_ctxt;
recording::statement *m_stmt;
};
/* The trivial constructor for rvalue_usage_validator. */
rvalue_usage_validator::rvalue_usage_validator (const char *api_funcname,
recording::context *ctxt,
recording::statement *stmt)
: m_api_funcname (api_funcname),
m_ctxt (ctxt),
m_stmt (stmt)
{
}
/* Verify that the given rvalue is in the correct scope. */
void
rvalue_usage_validator::visit (recording::rvalue *rvalue)
{
gcc_assert (m_stmt->get_block ());
recording::function *stmt_scope = m_stmt->get_block ()->get_function ();
/* Most rvalues don't have a scope (only locals and params). */
if (rvalue->get_scope ())
{
if (rvalue->get_scope () != stmt_scope)
m_ctxt->add_error
(rvalue->get_loc (),
"%s:"
" rvalue %s (type: %s)"
" has scope limited to function %s"
" but was used within function %s"
" (in statement: %s)",
m_api_funcname,
rvalue->get_debug_string (),
rvalue->get_type ()->get_debug_string (),
rvalue->get_scope ()->get_debug_string (),
stmt_scope->get_debug_string (),
m_stmt->get_debug_string ());
}
else
{
if (rvalue->dyn_cast_param ())
m_ctxt->add_error
(rvalue->get_loc (),
"%s:"
" param %s (type: %s)"
" was used within function %s"
" (in statement: %s)"
" but is not associated with any function",
m_api_funcname,
rvalue->get_debug_string (),
rvalue->get_type ()->get_debug_string (),
stmt_scope->get_debug_string (),
m_stmt->get_debug_string ());
}
}
/* Verify that it's valid to use this rvalue (and all expressions
in the tree below it) within the given statement.
For example, we must reject attempts to use a local from one
function within a different function here, or we'll get
an ICE deep inside toplev::main. */
void
recording::rvalue::verify_valid_within_stmt (const char *api_funcname, statement *s)
{
rvalue_usage_validator v (api_funcname,
s->get_context (),
s);
/* Verify that it's OK to use this rvalue within s. */
v.visit (this);
/* Traverse the expression tree below "this", verifying all rvalues
within it. */
visit_children (&v);
}
/* Set the scope of this rvalue to be the given function. This can only
be done once on a given rvalue. */
void
recording::rvalue::set_scope (function *scope)
{
gcc_assert (scope);
gcc_assert (m_scope == NULL);
m_scope = 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);
}
/* Return a debug string for the given rvalue, wrapping it in parentheses
if needed to mimic C's precedence rules, i.e. if OUTER_PREC is of
stronger precedence that this rvalue's precedence.
For example, given:
MULT
/ \
PLUS MINUS
/ \ / \
A B C D
we want to emit:
(A + B) * (C - D)
since MULT has strong precedence than PLUS and MINUS, whereas for:
PLUS
/ \
MULT DIVIDE
/ \ / \
A B C D
we can simply emit:
A * B + C / D
since PLUS has weaker precedence than MULT and DIVIDE. */
const char *
recording::rvalue::get_debug_string_parens (enum precedence outer_prec)
{
enum precedence this_prec = get_precedence ();
/* If this_prec has stronger precedence than outer_prec, we don't
need to wrap this in parens within the outer debug string.
Stronger precedences occur earlier than weaker within the enum,
so this is a less than test. Equal precedences don't need
parentheses. */
if (this_prec <= outer_prec)
return get_debug_string();
/* Otherwise, we need parentheses. */
/* Lazily-build and cache m_parenthesized_string. */
if (!m_parenthesized_string)
{
const char *debug_string = get_debug_string ();
m_parenthesized_string = string::from_printf (get_context (),
"(%s)",
debug_string);
}
gcc_assert (m_parenthesized_string);
return m_parenthesized_string->c_str ();
}
/* The implementation of class gcc::jit::recording::lvalue. */
/* Create a recording::new_access_field_of_lvalue instance and add it to
the lvalue's context's list of mementos.
Implements the post-error-checking part of
gcc_jit_lvalue_access_field. */
recording::lvalue *
recording::lvalue::access_field (recording::location *loc,
field *field)
{
recording::lvalue *result =
new access_field_of_lvalue (m_ctxt, loc, this, field);
m_ctxt->record (result);
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.
Implements the post-error-checking part of
gcc_jit_lvalue_get_address. */
recording::rvalue *
recording::lvalue::get_address (recording::location *loc)
{
recording::rvalue *result =
new get_address_of_lvalue (m_ctxt, loc, this);
m_ctxt->record (result);
return result;
}
/* The implementation of class gcc::jit::recording::param. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::param. */
void
recording::param::replay_into (replayer *r)
{
set_playback_obj (r->new_param (playback_location (r, m_loc),
m_type->playback_type (),
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. */
/* gcc::jit::recording::function's constructor. */
recording::function::function (context *ctxt,
recording::location *loc,
enum gcc_jit_function_kind kind,
type *return_type,
recording::string *name,
int num_params,
recording::param **params,
int is_variadic,
enum built_in_function builtin_id)
: memento (ctxt),
m_loc (loc),
m_kind (kind),
m_return_type (return_type),
m_name (name),
m_params (),
m_is_variadic (is_variadic),
m_builtin_id (builtin_id),
m_locals (),
m_blocks (),
m_fn_ptr_type (NULL)
{
for (int i = 0; i< num_params; i++)
{
param *param = params[i];
gcc_assert (param);
/* Associate each param with this function.
Verify that the param doesn't already have a function. */
if (param->get_scope ())
{
/* We've already rejected attempts to reuse a param between
different functions (within gcc_jit_context_new_function), so
if the param *does* already have a function, it must be being
reused within the params array for this function. We must
produce an error for this reuse (blocking the compile), since
otherwise we'd have an ICE later on. */
gcc_assert (this == param->get_scope ());
ctxt->add_error
(loc,
"gcc_jit_context_new_function:"
" parameter %s (type: %s)"
" is used more than once when creating function %s",
param->get_debug_string (),
param->get_type ()->get_debug_string (),
name->c_str ());
}
else
{
/* The normal, non-error case: associate this function with the
param. */
param->set_scope (this);
}
m_params.safe_push (param);
}
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::function. */
void
recording::function::replay_into (replayer *r)
{
/* Convert m_params to a vec of playback param. */
auto_vec <playback::param *> params;
int i;
recording::param *param;
params.create (m_params.length ());
FOR_EACH_VEC_ELT (m_params, i, param)
params.safe_push (param->playback_param ());
set_playback_obj (r->new_function (playback_location (r, m_loc),
m_kind,
m_return_type->playback_type (),
m_name->c_str (),
&params,
m_is_variadic,
m_builtin_id));
}
/* Create a recording::local instance and add it to
the functions's context's list of mementos, and to the function's
list of locals.
Implements the post-error-checking part of
gcc_jit_function_new_local. */
recording::lvalue *
recording::function::new_local (recording::location *loc,
type *type,
const char *name)
{
local *result = new local (this, loc, type, new_string (name));
m_ctxt->record (result);
m_locals.safe_push (result);
return result;
}
/* Create a recording::block instance and add it to
the functions's context's list of mementos, and to the function's
list of blocks.
Implements the post-error-checking part of
gcc_jit_function_new_block. */
recording::block*
recording::function::new_block (const char *name)
{
gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
recording::block *result =
new recording::block (this, m_blocks.length (), new_string (name));
m_ctxt->record (result);
m_blocks.safe_push (result);
return result;
}
/* Override the default implementation of
recording::memento::write_to_dump by dumping a C-like
representation of the function; either like a prototype
for GCC_JIT_FUNCTION_IMPORTED, or like a full definition for
all other kinds of function. */
void
recording::function::write_to_dump (dump &d)
{
switch (m_kind)
{
default: gcc_unreachable ();
case GCC_JIT_FUNCTION_EXPORTED:
case GCC_JIT_FUNCTION_IMPORTED:
d.write ("extern ");
break;
case GCC_JIT_FUNCTION_INTERNAL:
d.write ("static ");
break;
case GCC_JIT_FUNCTION_ALWAYS_INLINE:
d.write ("static inline ");
break;
}
d.write ("%s\n", m_return_type->get_debug_string ());
if (d.update_locations ())
m_loc = d.make_location ();
d.write ("%s (", get_debug_string ());
int i;
recording::param *param;
FOR_EACH_VEC_ELT (m_params, i, param)
{
if (i > 0)
d.write (", ");
d.write ("%s %s",
param->get_type ()->get_debug_string (),
param->get_debug_string ());
}
d.write (")");
if (m_kind == GCC_JIT_FUNCTION_IMPORTED)
{
d.write ("; /* (imported) */\n\n");
}
else
{
int i;
local *var = NULL;
block *b;
d.write ("\n{\n");
/* Write locals: */
FOR_EACH_VEC_ELT (m_locals, i, var)
var->write_to_dump (d);
if (m_locals.length ())
d.write ("\n");
/* Write each block: */
FOR_EACH_VEC_ELT (m_blocks, i, b)
{
if (i > 0)
d.write ("\n");
b->write_to_dump (d);
}
d.write ("}\n\n");
}
}
/* Pre-compilation validation of a function, for those things we can't
check until the context is (supposedly) fully-populated. */
void
recording::function::validate ()
{
/* Complain about empty functions with non-void return type. */
if (m_kind != GCC_JIT_FUNCTION_IMPORTED
&& m_return_type != m_ctxt->get_type (GCC_JIT_TYPE_VOID))
if (m_blocks.length () == 0)
m_ctxt->add_error (m_loc,
"function %s returns non-void (type: %s)"
" but has no blocks",
get_debug_string (),
m_return_type->get_debug_string ());
/* Check that all blocks are terminated. */
int num_invalid_blocks = 0;
{
int i;
block *b;
FOR_EACH_VEC_ELT (m_blocks, i, b)
if (!b->validate ())
num_invalid_blocks++;
}
/* Check that all blocks are reachable. */
if (!m_ctxt->get_inner_bool_option
(INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS)
&& m_blocks.length () > 0 && num_invalid_blocks == 0)
{
/* Iteratively walk the graph of blocks, marking their "m_is_reachable"
flag, starting at the initial block. */
auto_vec<block *> worklist (m_blocks.length ());
worklist.safe_push (m_blocks[0]);
while (worklist.length () > 0)
{
block *b = worklist.pop ();
b->m_is_reachable = true;
/* Add successor blocks that aren't yet marked to the worklist. */
/* We checked that each block has a terminating statement above . */
vec <block *> successors = b->get_successor_blocks ();
int i;
block *succ;
FOR_EACH_VEC_ELT (successors, i, succ)
if (!succ->m_is_reachable)
worklist.safe_push (succ);
successors.release ();
}
/* Now complain about any blocks that haven't been marked. */
{
int i;
block *b;
FOR_EACH_VEC_ELT (m_blocks, i, b)
if (!b->m_is_reachable)
m_ctxt->add_error (b->get_loc (),
"unreachable block: %s",
b->get_debug_string ());
}
}
}
/* Implements the post-error-checking part of
gcc_jit_function_dump_to_dot. */
void
recording::function::dump_to_dot (const char *path)
{
FILE *fp = fopen (path, "w");
if (!fp)
return;
pretty_printer the_pp;
the_pp.buffer->stream = fp;
pretty_printer *pp = &the_pp;
pp_printf (pp, "digraph %s", get_debug_string ());
pp_string (pp, " {\n");
/* Blocks: */
{
int i;
block *b;
FOR_EACH_VEC_ELT (m_blocks, i, b)
b->dump_to_dot (pp);
}
/* Edges: */
{
int i;
block *b;
FOR_EACH_VEC_ELT (m_blocks, i, b)
b->dump_edges_to_dot (pp);
}
pp_string (pp, "}\n");
pp_flush (pp);
fclose (fp);
}
/* Implements the post-error-checking part of
gcc_jit_function_get_address. */
recording::rvalue *
recording::function::get_address (recording::location *loc)
{
/* Lazily create and cache the function pointer type. */
if (!m_fn_ptr_type)
{
/* Make a recording::function_type for this function. */
auto_vec <recording::type *> param_types (m_params.length ());
unsigned i;
recording::param *param;
FOR_EACH_VEC_ELT (m_params, i, param)
param_types.safe_push (param->get_type ());
recording::function_type *fn_type
= m_ctxt->new_function_type (m_return_type,
m_params.length (),
param_types.address (),
m_is_variadic);
m_fn_ptr_type = fn_type->get_pointer ();
}
gcc_assert (m_fn_ptr_type);
rvalue *result = new function_pointer (get_context (), loc, this, m_fn_ptr_type);
m_ctxt->record (result);
return result;
}
/* Implementation of recording::memento::make_debug_string for
functions. */
recording::string *
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
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_add_eval. */
recording::statement *
recording::block::add_eval (recording::location *loc,
recording::rvalue *rvalue)
{
statement *result = new eval (this, loc, rvalue);
m_ctxt->record (result);
m_statements.safe_push (result);
return result;
}
/* Create a recording::assignment instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_add_assignment. */
recording::statement *
recording::block::add_assignment (recording::location *loc,
recording::lvalue *lvalue,
recording::rvalue *rvalue)
{
statement *result = new assignment (this, loc, lvalue, rvalue);
m_ctxt->record (result);
m_statements.safe_push (result);
return result;
}
/* Create a recording::assignment_op instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_add_assignment_op. */
recording::statement *
recording::block::add_assignment_op (recording::location *loc,
recording::lvalue *lvalue,
enum gcc_jit_binary_op op,
recording::rvalue *rvalue)
{
statement *result = new assignment_op (this, loc, lvalue, op, rvalue);
m_ctxt->record (result);
m_statements.safe_push (result);
return result;
}
/* Create a recording::comment instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_add_comment. */
recording::statement *
recording::block::add_comment (recording::location *loc,
const char *text)
{
statement *result = new comment (this, loc, new_string (text));
m_ctxt->record (result);
m_statements.safe_push (result);
return result;
}
/* Create a recording::extended_asm_simple instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_add_extended_asm. */
recording::extended_asm *
recording::block::add_extended_asm (location *loc,
const char *asm_template)
{
extended_asm *result
= new extended_asm_simple (this, loc, new_string (asm_template));
m_ctxt->record (result);
m_statements.safe_push (result);
return result;
}
/* Create a recording::end_with_conditional instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_end_with_conditional. */
recording::statement *
recording::block::end_with_conditional (recording::location *loc,
recording::rvalue *boolval,
recording::block *on_true,
recording::block *on_false)
{
statement *result = new conditional (this, loc, boolval, on_true, on_false);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Create a recording::end_with_jump instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_end_with_jump. */
recording::statement *
recording::block::end_with_jump (recording::location *loc,
recording::block *target)
{
statement *result = new jump (this, loc, target);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Create a recording::end_with_return instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the post-error-checking parts of
gcc_jit_block_end_with_return and
gcc_jit_block_end_with_void_return. */
recording::statement *
recording::block::end_with_return (recording::location *loc,
recording::rvalue *rvalue)
{
/* This is used by both gcc_jit_function_add_return and
gcc_jit_function_add_void_return; rvalue will be non-NULL for
the former and NULL for the latter. */
statement *result = new return_ (this, loc, rvalue);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Create a recording::switch_ instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_end_with_switch. */
recording::statement *
recording::block::end_with_switch (recording::location *loc,
recording::rvalue *expr,
recording::block *default_block,
int num_cases,
recording::case_ **cases)
{
statement *result = new switch_ (this, loc,
expr,
default_block,
num_cases,
cases);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Create a recording::extended_asm_goto instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_end_with_extended_asm_goto. */
recording::extended_asm *
recording::block::end_with_extended_asm_goto (location *loc,
const char *asm_template,
int num_goto_blocks,
block **goto_blocks,
block *fallthrough_block)
{
extended_asm *result
= new extended_asm_goto (this, loc, new_string (asm_template),
num_goto_blocks, goto_blocks,
fallthrough_block);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Override the default implementation of
recording::memento::write_to_dump for blocks by writing
an unindented block name as a label, followed by the indented
statements:
BLOCK_NAME:
STATEMENT_1;
STATEMENT_2;
...
STATEMENT_N; */
void
recording::block::write_to_dump (dump &d)
{
d.write ("%s:\n", get_debug_string ());
int i;
statement *s;
FOR_EACH_VEC_ELT (m_statements, i, s)
s->write_to_dump (d);
}
/* Validate a block by ensuring that it has been terminated. */
bool
recording::block::validate ()
{
/* Check for termination. */
if (!has_been_terminated ())
{
statement *stmt = get_last_statement ();
location *loc = stmt ? stmt->get_loc () : NULL;
m_func->get_context ()->add_error (loc,
"unterminated block in %s: %s",
m_func->get_debug_string (),
get_debug_string ());
return false;
}
return true;
}
/* Get the source-location of a block by using that of the first
statement within it, if any. */
recording::location *
recording::block::get_loc () const
{
recording::statement *stmt = get_first_statement ();
if (stmt)
return stmt->get_loc ();
else
return NULL;
}
/* Get the first statement within a block, if any. */
recording::statement *
recording::block::get_first_statement () const
{
if (m_statements.length ())
return m_statements[0];
else
return NULL;
}
/* Get the last statement within a block, if any. */
recording::statement *
recording::block::get_last_statement () const
{
if (m_statements.length ())
return m_statements[m_statements.length () - 1];
else
return NULL;
}
/* Assuming that this block has been terminated, get the successor blocks
as a vector. Ownership of the vector transfers to the caller, which
must call its release () method.
Used when validating functions, and when dumping dot representations
of them. */
vec <recording::block *>
recording::block::get_successor_blocks () const
{
gcc_assert (m_has_been_terminated);
statement *last_statement = get_last_statement ();
gcc_assert (last_statement);
return last_statement->get_successor_blocks ();
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::block. */
void
recording::block::replay_into (replayer *)
{
set_playback_obj (m_func->playback_function ()
->new_block (playback_string (m_name)));
}
/* Implementation of recording::memento::make_debug_string for
blocks. */
recording::string *
recording::block::make_debug_string ()
{
if (m_name)
return m_name;
else
return string::from_printf (m_ctxt,
"<UNNAMED BLOCK %p>",
(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");
}
/* Disable warnings about missing quoting in GCC diagnostics for
the pp_printf calls. Their format strings deliberately don't
follow GCC diagnostic conventions. */
#if __GNUC__ >= 10
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-diag"
#endif
/* Dump a block in graphviz form into PP, capturing the block name (if
any) and the statements. */
void
recording::block::dump_to_dot (pretty_printer *pp)
{
pp_printf (pp,
("\tblock_%d "
"[shape=record,style=filled,fillcolor=white,label=\"{"),
m_index);
pp_write_text_to_stream (pp);
if (m_name)
{
pp_string (pp, m_name->c_str ());
pp_string (pp, ":");
pp_newline (pp);
pp_write_text_as_dot_label_to_stream (pp, true /*for_record*/);
}
int i;
statement *s;
FOR_EACH_VEC_ELT (m_statements, i, s)
{
pp_string (pp, s->get_debug_string ());
pp_newline (pp);
pp_write_text_as_dot_label_to_stream (pp, true /*for_record*/);
}
pp_string (pp,
"}\"];\n\n");
pp_flush (pp);
}
/* Dump the out-edges of the block in graphviz form into PP. */
void
recording::block::dump_edges_to_dot (pretty_printer *pp)
{
vec <block *> successors = get_successor_blocks ();
int i;
block *succ;
FOR_EACH_VEC_ELT (successors, i, succ)
pp_printf (pp,
"\tblock_%d:s -> block_%d:n;\n",
m_index, succ->m_index);
successors.release ();
}
#if __GNUC__ >= 10
# pragma GCC diagnostic pop
#endif
/* The implementation of class gcc::jit::recording::global. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::global. */
void
recording::global::replay_into (replayer *r)
{
set_playback_obj (
m_initializer
? r->new_global_initialized (playback_location (r, m_loc),
m_kind,
m_type->playback_type (),
m_type->dereference ()->get_size (),
m_initializer_num_bytes
/ m_type->dereference ()->get_size (),
m_initializer,
playback_string (m_name))
: r->new_global (playback_location (r, m_loc),
m_kind,
m_type->playback_type (),
playback_string (m_name)));
}
/* Override the default implementation of
recording::memento::write_to_dump for globals.
This will be of the form:
GCC_JIT_GLOBAL_EXPORTED:
"TYPE NAME;"
e.g. "int foo;"
GCC_JIT_GLOBAL_INTERNAL:
"static TYPE NAME;"
e.g. "static int foo;"
GCC_JIT_GLOBAL_IMPORTED:
"extern TYPE NAME;"
e.g. "extern int foo;"
These are written to the top of the dump by
recording::context::dump_to_file. */
void
recording::global::write_to_dump (dump &d)
{
if (d.update_locations ())
m_loc = d.make_location ();
switch (m_kind)
{
default:
gcc_unreachable ();
case GCC_JIT_GLOBAL_EXPORTED:
break;
case GCC_JIT_GLOBAL_INTERNAL:
d.write ("static ");
break;
case GCC_JIT_GLOBAL_IMPORTED:
d.write ("extern ");
break;
}
d.write ("%s %s",
m_type->get_debug_string (),
get_debug_string ());
if (!m_initializer)
{
d.write (";\n");
return;
}
d.write ("=\n { ");
const unsigned char *p = (const unsigned char *)m_initializer;
for (size_t i = 0; i < m_initializer_num_bytes; i++)
{
d.write ("0x%x, ", p[i]);
if (i && !(i % 64))
d.write ("\n ");
}
d.write ("};\n");
}
/* 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"
};
template <typename T>
void
recording::global::write_initializer_reproducer (const char *id, reproducer &r)
{
const char *init_id = r.make_tmp_identifier ("init_for", this);
r.write (" %s %s[] =\n {",
m_type->dereference ()->get_debug_string (),
init_id);
const T *p = (const T *)m_initializer;
for (size_t i = 0; i < m_initializer_num_bytes / sizeof (T); i++)
{
r.write ("%" PRIu64 ", ", (uint64_t)p[i]);
if (i && !(i % 64))
r.write ("\n ");
}
r.write ("};\n");
r.write (" gcc_jit_global_set_initializer (%s, %s, sizeof (%s));\n",
id, init_id, init_id);
}
/* 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 ());
if (m_initializer)
switch (m_type->dereference ()->get_size ())
{
case 1:
write_initializer_reproducer<uint8_t> (id, r);
break;
case 2:
write_initializer_reproducer<uint16_t> (id, r);
break;
case 4:
write_initializer_reproducer<uint32_t> (id, r);
break;
case 8:
write_initializer_reproducer<uint64_t> (id, r);
break;
default:
/* This function is serving on sizes returned by 'get_size',
these are all covered by the previous cases. */
gcc_unreachable ();
}
}
/* The implementation of the various const-handling classes:
gcc::jit::recording::memento_of_new_rvalue_from_const <HOST_TYPE>. */
/* Explicit specialization of the various mementos we're interested in. */
template class recording::memento_of_new_rvalue_from_const <int>;
template class recording::memento_of_new_rvalue_from_const <long>;
template class recording::memento_of_new_rvalue_from_const <double>;
template class recording::memento_of_new_rvalue_from_const <void *>;
/* Implementation of the pure virtual hook recording::memento::replay_into
for recording::memento_of_new_rvalue_from_const <HOST_TYPE>. */
template <typename HOST_TYPE>
void
recording::
memento_of_new_rvalue_from_const <HOST_TYPE>::replay_into (replayer *r)
{
set_playback_obj
(r->new_rvalue_from_const <HOST_TYPE> (m_type->playback_type (),
m_value));
}
/* 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
doesn't abbreviate things by entering the "recording" namespace.
However, these specializations are required to be in the same namespace
as the template, hence we now have to enter the gcc::jit::recording
namespace. */
namespace recording
{
/* The make_debug_string specialization for <int>, which renders it as
(TARGET_TYPE)LITERAL
e.g.
"(int)42". */
template <>
string *
memento_of_new_rvalue_from_const <int>::make_debug_string ()
{
return string::from_printf (m_ctxt,
"(%s)%i",
m_type->get_debug_string (),
m_value);
}
/* The get_wide_int specialization for <int>. */
template <>
bool
memento_of_new_rvalue_from_const <int>::get_wide_int (wide_int *out) const
{
*out = wi::shwi (m_value, sizeof (m_value) * 8);
return true;
}
/* 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.
"(long)42". */
template <>
string *
memento_of_new_rvalue_from_const <long>::make_debug_string ()
{
return string::from_printf (m_ctxt,
"(%s)%li",
m_type->get_debug_string (),
m_value);
}
/* The get_wide_int specialization for <long>. */
template <>
bool
memento_of_new_rvalue_from_const <long>::get_wide_int (wide_int *out) const
{
*out = wi::shwi (m_value, sizeof (m_value) * 8);
return true;
}
/* 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.
"(float)42.0". */
template <>
string *
memento_of_new_rvalue_from_const <double>::make_debug_string ()
{
return string::from_printf (m_ctxt,
"(%s)%f",
m_type->get_debug_string (),
m_value);
}
/* The get_wide_int specialization for <double>. */
template <>
bool
memento_of_new_rvalue_from_const <double>::get_wide_int (wide_int *) const
{
return false;
}
/* 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.
"(int *)0xdeadbeef"
Zero is rendered as NULL e.g.
"(int *)NULL". */
template <>
string *
memento_of_new_rvalue_from_const <void *>::make_debug_string ()
{
if (m_value != NULL)
return string::from_printf (m_ctxt,
"(%s)%p",
m_type->get_debug_string (), m_value);
else
return string::from_printf (m_ctxt,
"(%s)NULL",
m_type->get_debug_string ());
}
/* The get_wide_int specialization for <void *>. */
template <>
bool
memento_of_new_rvalue_from_const <void *>::get_wide_int (wide_int *) const
{
return false;
}
/* 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
/* The implementation of class gcc::jit::recording::memento_of_new_string_literal. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::memento_of_new_string_literal. */
void
recording::memento_of_new_string_literal::replay_into (replayer *r)
{
set_playback_obj (r->new_string_literal (m_value->c_str ()));
}
/* Implementation of recording::memento::make_debug_string for
string literals. */
recording::string *
recording::memento_of_new_string_literal::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s",
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::memento_of_new_rvalue_from_vector. */
/* The constructor for
gcc::jit::recording::memento_of_new_rvalue_from_vector. */
recording::memento_of_new_rvalue_from_vector::
memento_of_new_rvalue_from_vector (context *ctxt,
location *loc,
vector_type *type,
rvalue **elements)
: rvalue (ctxt, loc, type),
m_vector_type (type),
m_elements ()
{
for (unsigned i = 0; i < type->get_num_units (); i++)
m_elements.safe_push (elements[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::memento_of_new_rvalue_from_vector. */
void
recording::memento_of_new_rvalue_from_vector::replay_into (replayer *r)
{
auto_vec<playback::rvalue *> playback_elements;
playback_elements.create (m_elements.length ());
for (unsigned i = 0; i< m_elements.length (); i++)
playback_elements.safe_push (m_elements[i]->playback_rvalue ());
set_playback_obj (r->new_rvalue_from_vector (playback_location (r, m_loc),
m_type->playback_type (),
playback_elements));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::memento_of_new_rvalue_from_vector. */
void
recording::memento_of_new_rvalue_from_vector::visit_children (rvalue_visitor *v)
{
for (unsigned i = 0; i< m_elements.length (); i++)
v->visit (m_elements[i]);
}
/* Implementation of recording::memento::make_debug_string for
vectors. */
recording::string *
recording::memento_of_new_rvalue_from_vector::make_debug_string ()
{
comma_separated_string elements (m_elements, get_precedence ());
/* Now build a string. */
string *result = string::from_printf (m_ctxt,
"{%s}",
elements.as_char_ptr ());
return result;
}
/* Implementation of recording::memento::write_reproducer for
vectors. */
void
recording::memento_of_new_rvalue_from_vector::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "vector");
const char *elements_id = r.make_tmp_identifier ("elements_for_", this);
r.write (" gcc_jit_rvalue *%s[%i] = {\n",
elements_id,
m_elements.length ());
for (unsigned i = 0; i< m_elements.length (); i++)
r.write (" %s,\n", r.get_identifier_as_rvalue (m_elements[i]));
r.write (" };\n");
r.write (" gcc_jit_rvalue *%s =\n"
" gcc_jit_context_new_rvalue_from_vector (%s, /* gcc_jit_context *ctxt */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s, /* gcc_jit_type *vec_type */\n"
" %i, /* size_t num_elements */ \n"
" %s); /* gcc_jit_rvalue **elements*/\n",
id,
r.get_identifier (get_context ()),
r.get_identifier (m_loc),
r.get_identifier (m_vector_type),
m_elements.length (),
elements_id);
}
/* The implementation of class gcc::jit::recording::unary_op. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::unary_op. */
void
recording::unary_op::replay_into (replayer *r)
{
set_playback_obj (r->new_unary_op (playback_location (r, m_loc),
m_op,
get_type ()->playback_type (),
m_a->playback_rvalue ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::unary_op. */
void
recording::unary_op::visit_children (rvalue_visitor *v)
{
v->visit (m_a);
}
/* Implementation of recording::memento::make_debug_string for
unary ops. */
static const char * const unary_op_strings[] = {
"-", /* GCC_JIT_UNARY_OP_MINUS */
"~", /* GCC_JIT_UNARY_OP_BITWISE_NEGATE */
"!", /* GCC_JIT_UNARY_OP_LOGICAL_NEGATE */
"abs ", /* GCC_JIT_UNARY_OP_ABS */
};
recording::string *
recording::unary_op::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s(%s)",
unary_op_strings[m_op],
m_a->get_debug_string ());
}
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
for recording::binary_op. */
void
recording::binary_op::replay_into (replayer *r)
{
set_playback_obj (r->new_binary_op (playback_location (r, m_loc),
m_op,
get_type ()->playback_type (),
m_a->playback_rvalue (),
m_b->playback_rvalue ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::binary_op. */
void
recording::binary_op::visit_children (rvalue_visitor *v)
{
v->visit (m_a);
v->visit (m_b);
}
/* Implementation of recording::memento::make_debug_string for
binary ops. */
static const char * const binary_op_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 */
};
recording::string *
recording::binary_op::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s %s %s",
m_a->get_debug_string_parens (prec),
binary_op_strings[m_op],
m_b->get_debug_string_parens (prec));
}
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));
}
namespace recording {
static const enum precedence binary_op_precedence[] = {
PRECEDENCE_ADDITIVE, /* GCC_JIT_BINARY_OP_PLUS */
PRECEDENCE_ADDITIVE, /* GCC_JIT_BINARY_OP_MINUS */
PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_MULT */
PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_DIVIDE */
PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_MODULO */
PRECEDENCE_BITWISE_AND, /* GCC_JIT_BINARY_OP_BITWISE_AND */
PRECEDENCE_BITWISE_XOR, /* GCC_JIT_BINARY_OP_BITWISE_XOR */
PRECEDENCE_BITWISE_IOR, /* GCC_JIT_BINARY_OP_BITWISE_OR */
PRECEDENCE_LOGICAL_AND, /* GCC_JIT_BINARY_OP_LOGICAL_AND */
PRECEDENCE_LOGICAL_OR, /* GCC_JIT_BINARY_OP_LOGICAL_OR */
PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_LSHIFT */
PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_RSHIFT */
};
} /* namespace recording */
enum recording::precedence
recording::binary_op::get_precedence () const
{
return binary_op_precedence[m_op];
}
/* The implementation of class gcc::jit::recording::comparison. */
/* Implementation of recording::memento::make_debug_string for
comparisons. */
static const char * const comparison_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 */
};
recording::string *
recording::comparison::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s %s %s",
m_a->get_debug_string_parens (prec),
comparison_strings[m_op],
m_b->get_debug_string_parens (prec));
}
/* 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. */
void
recording::comparison::replay_into (replayer *r)
{
set_playback_obj (r->new_comparison (playback_location (r, m_loc),
m_op,
m_a->playback_rvalue (),
m_b->playback_rvalue ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::comparison. */
void
recording::comparison::visit_children (rvalue_visitor *v)
{
v->visit (m_a);
v->visit (m_b);
}
namespace recording {
static const enum precedence comparison_precedence[] =
{
PRECEDENCE_EQUALITY, /* GCC_JIT_COMPARISON_EQ */
PRECEDENCE_EQUALITY, /* GCC_JIT_COMPARISON_NE */
PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_LT */
PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_LE */
PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_GT */
PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_GE */
};
} /* namespace recording */
enum recording::precedence
recording::comparison::get_precedence () const
{
return comparison_precedence[m_op];
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::cast. */
void
recording::cast::replay_into (replayer *r)
{
set_playback_obj (r->new_cast (playback_location (r, m_loc),
m_rvalue->playback_rvalue (),
get_type ()->playback_type ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::cast. */
void
recording::cast::visit_children (rvalue_visitor *v)
{
v->visit (m_rvalue);
}
/* Implementation of recording::memento::make_debug_string for
casts. */
recording::string *
recording::cast::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"(%s)%s",
get_type ()->get_debug_string (),
m_rvalue->get_debug_string_parens (prec));
}
/* 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::base_call. */
/* The constructor for gcc::jit::recording::base_call. */
recording::base_call::base_call (context *ctxt,
location *loc,
type *type_,
int numargs,
rvalue **args)
: rvalue (ctxt, loc, type_),
m_args (),
m_require_tail_call (0)
{
for (int i = 0; i< numargs; i++)
m_args.safe_push (args[i]);
}
/* Subroutine for use by call and call_though_ptr's write_reproducer
methods. */
void
recording::base_call::write_reproducer_tail_call (reproducer &r,
const char *id)
{
if (m_require_tail_call)
{
r.write (" gcc_jit_rvalue_set_bool_require_tail_call (%s, /* gcc_jit_rvalue *call*/\n"
" %i); /* int require_tail_call*/\n",
id,
1);
}
}
/* The implementation of class gcc::jit::recording::call. */
/* The constructor for gcc::jit::recording::call. */
recording::call::call (recording::context *ctxt,
recording::location *loc,
recording::function *func,
int numargs,
rvalue **args)
: base_call (ctxt, loc, func->get_return_type (), numargs, args),
m_func (func)
{
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::call. */
void
recording::call::replay_into (replayer *r)
{
auto_vec<playback::rvalue *> playback_args;
playback_args.create (m_args.length ());
for (unsigned i = 0; i< m_args.length (); i++)
playback_args.safe_push (m_args[i]->playback_rvalue ());
set_playback_obj (r->new_call (playback_location (r, m_loc),
m_func->playback_function (),
&playback_args,
m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::call. */
void
recording::call::visit_children (rvalue_visitor *v)
{
for (unsigned i = 0; i< m_args.length (); i++)
v->visit (m_args[i]);
}
/* Implementation of recording::memento::make_debug_string for
function calls. */
recording::string *
recording::call::make_debug_string ()
{
/* First, build a buffer for the arguments. */
comma_separated_string args (m_args, get_precedence ());
/* ...and use it to get the string for the call as a whole. */
string *result = string::from_printf (m_ctxt,
"%s (%s)",
m_func->get_debug_string (),
args.as_char_ptr ());
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);
write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::call_through_ptr. */
/* The constructor for recording::call_through_ptr. */
recording::call_through_ptr::call_through_ptr (recording::context *ctxt,
recording::location *loc,
recording::rvalue *fn_ptr,
int numargs,
rvalue **args)
: base_call (ctxt, loc,
fn_ptr->get_type ()->dereference ()
->as_a_function_type ()->get_return_type (),
numargs, args),
m_fn_ptr (fn_ptr)
{
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::call_through_ptr. */
void
recording::call_through_ptr::replay_into (replayer *r)
{
auto_vec<playback::rvalue *> playback_args;
playback_args.create (m_args.length ());
for (unsigned i = 0; i< m_args.length (); i++)
playback_args.safe_push (m_args[i]->playback_rvalue ());
set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
m_fn_ptr->playback_rvalue (),
&playback_args,
m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::call_through_ptr. */
void
recording::call_through_ptr::visit_children (rvalue_visitor *v)
{
v->visit (m_fn_ptr);
for (unsigned i = 0; i< m_args.length (); i++)
v->visit (m_args[i]);
}
/* Implementation of recording::memento::make_debug_string for
calls through function ptrs. */
recording::string *
recording::call_through_ptr::make_debug_string ()
{
enum precedence prec = get_precedence ();
/* First, build a buffer for the arguments. */
/* Calculate length of said buffer. */
size_t sz = 1; /* nil terminator */
for (unsigned i = 0; i< m_args.length (); i++)
{
sz += strlen (m_args[i]->get_debug_string_parens (prec));
sz += 2; /* ", " separator */
}
/* Now allocate and populate the buffer. */
char *argbuf = new char[sz];
size_t len = 0;
for (unsigned i = 0; i< m_args.length (); i++)
{
strcpy (argbuf + len, m_args[i]->get_debug_string_parens (prec));
len += strlen (m_args[i]->get_debug_string_parens (prec));
if (i + 1 < m_args.length ())
{
strcpy (argbuf + len, ", ");
len += 2;
}
}
argbuf[len] = '\0';
/* ...and use it to get the string for the call as a whole. */
string *result = string::from_printf (m_ctxt,
"%s (%s)",
m_fn_ptr->get_debug_string_parens (prec),
argbuf);
delete[] argbuf;
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);
write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::array_access. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::array_access. */
void
recording::array_access::replay_into (replayer *r)
{
set_playback_obj (
r->new_array_access (playback_location (r, m_loc),
m_ptr->playback_rvalue (),
m_index->playback_rvalue ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::array_access. */
void
recording::array_access::visit_children (rvalue_visitor *v)
{
v->visit (m_ptr);
v->visit (m_index);
}
/* Implementation of recording::memento::make_debug_string for
array accesses. */
recording::string *
recording::array_access::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s[%s]",
m_ptr->get_debug_string_parens (prec),
m_index->get_debug_string_parens (prec));
}
/* 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
for recording::access_field_of_lvalue. */
void
recording::access_field_of_lvalue::replay_into (replayer *r)
{
set_playback_obj (
m_lvalue->playback_lvalue ()
->access_field (playback_location (r, m_loc),
m_field->playback_field ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::access_field_of_lvalue. */
void
recording::access_field_of_lvalue::visit_children (rvalue_visitor *v)
{
v->visit (m_lvalue);
}
/* Implementation of recording::memento::make_debug_string for
accessing a field of an lvalue. */
recording::string *
recording::access_field_of_lvalue::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s.%s",
m_lvalue->get_debug_string_parens (prec),
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
for recording::access_field_rvalue. */
void
recording::access_field_rvalue::replay_into (replayer *r)
{
set_playback_obj (
m_rvalue->playback_rvalue ()
->access_field (playback_location (r, m_loc),
m_field->playback_field ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::access_field_rvalue. */
void
recording::access_field_rvalue::visit_children (rvalue_visitor *v)
{
v->visit (m_rvalue);
}
/* Implementation of recording::memento::make_debug_string for
accessing a field of an rvalue. */
recording::string *
recording::access_field_rvalue::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s.%s",
m_rvalue->get_debug_string_parens (prec),
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. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::dereference_field_rvalue. */
void
recording::dereference_field_rvalue::replay_into (replayer *r)
{
set_playback_obj (
m_rvalue->playback_rvalue ()->
dereference_field (playback_location (r, m_loc),
m_field->playback_field ()));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::dereference_field_rvalue. */
void
recording::dereference_field_rvalue::visit_children (rvalue_visitor *v)
{
v->visit (m_rvalue);
}
/* Implementation of recording::memento::make_debug_string for
dereferencing a field of an rvalue. */
recording::string *
recording::dereference_field_rvalue::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"%s->%s",
m_rvalue->get_debug_string_parens (prec),
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
for recording::dereference_rvalue. */
void
recording::dereference_rvalue::replay_into (replayer *r)
{
set_playback_obj (
m_rvalue->playback_rvalue ()->
dereference (playback_location (r, m_loc)));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::dereference_rvalue. */
void
recording::dereference_rvalue::visit_children (rvalue_visitor *v)
{
v->visit (m_rvalue);
}
/* Implementation of recording::memento::make_debug_string for
dereferencing an rvalue. */
recording::string *
recording::dereference_rvalue::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"*%s",
m_rvalue->get_debug_string_parens (prec));
}
/* 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
for recording::get_address_of_lvalue. */
void
recording::get_address_of_lvalue::replay_into (replayer *r)
{
set_playback_obj (
m_lvalue->playback_lvalue ()->
get_address (playback_location (r, m_loc)));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
for recording::get_address_of_lvalue. */
void
recording::get_address_of_lvalue::visit_children (rvalue_visitor *v)
{
v->visit (m_lvalue);
}
/* Implementation of recording::memento::make_debug_string for
getting the address of an lvalue. */
recording::string *
recording::get_address_of_lvalue::make_debug_string ()
{
enum precedence prec = get_precedence ();
return string::from_printf (m_ctxt,
"&%s",
m_lvalue->get_debug_string_parens (prec));
}
/* 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::function_pointer. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::function_pointer. */
void
recording::function_pointer::replay_into (replayer *r)
{
set_playback_obj (
m_fn->playback_function ()->
get_address (playback_location (r, m_loc)));
}
void
recording::function_pointer::visit_children (rvalue_visitor *)
{
/* Empty. */
}
/* Implementation of recording::memento::make_debug_string for
getting the address of an lvalue. */
recording::string *
recording::function_pointer::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s",
m_fn->get_debug_string ());
}
/* Implementation of recording::memento::write_reproducer for
function_pointer. */
void
recording::function_pointer::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "address_of");
r.write (" gcc_jit_rvalue *%s =\n"
" gcc_jit_function_get_address (%s, /* gcc_jit_function *fn */\n"
" %s); /* gcc_jit_location *loc */\n",
id,
r.get_identifier (m_fn),
r.get_identifier (m_loc));
}
/* The implementation of class gcc::jit::recording::local. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::local. */
void
recording::local::replay_into (replayer *r)
{
set_playback_obj (
m_func->playback_function ()
->new_local (playback_location (r, m_loc),
m_type->playback_type (),
playback_string (m_name)));
}
/* Override the default implementation of
recording::memento::write_to_dump for locals by writing
TYPE NAME;
for use at the top of the function body as if it were a
declaration. */
void
recording::local::write_to_dump (dump &d)
{
if (d.update_locations ())
m_loc = d.make_location ();
d.write(" %s %s;\n",
m_type->get_debug_string (),
get_debug_string ());
}
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
gcc::jit::recording::statement::get_successor_blocks
since this vfunc must only ever be called on terminator
statements. */
vec <recording::block *>
recording::statement::get_successor_blocks () const
{
/* The base class implementation is for non-terminating statements,
and thus should never be called. */
gcc_unreachable ();
vec <block *> result;
result.create (0);
return result;
}
/* Extend the default implementation of
recording::memento::write_to_dump for statements by (if requested)
updating the location of the statement to the current location in
the dumpfile. */
void
recording::statement::write_to_dump (dump &d)
{
memento::write_to_dump (d);
if (d.update_locations ())
m_loc = d.make_location ();
}
/* The implementation of class gcc::jit::recording::eval. */
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::eval. */
void
recording::eval::replay_into (replayer *r)
{
playback_block (get_block ())
->add_eval (playback_location (r),
m_rvalue->playback_rvalue ());
}
/* Implementation of recording::memento::make_debug_string for
an eval statement. */
recording::string *
recording::eval::make_debug_string ()
{
return string::from_printf (m_ctxt,
"(void)%s;",
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
for recording::assignment. */
void
recording::assignment::replay_into (replayer *r)
{
playback_block (get_block ())
->add_assignment (playback_location (r),
m_lvalue->playback_lvalue (),
m_rvalue->playback_rvalue ());
}
/* Implementation of recording::memento::make_debug_string for
an assignment statement. */
recording::string *
recording::assignment::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s = %s;",
m_lvalue->get_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
for recording::assignment_op. */
void
recording::assignment_op::replay_into (replayer *r)
{
playback::type *result_type =
m_lvalue->playback_lvalue ()->get_type ();
playback::rvalue *binary_op =
r->new_binary_op (playback_location (r),
m_op,
result_type,
m_lvalue->playback_rvalue (),
m_rvalue->playback_rvalue ());
playback_block (get_block ())
->add_assignment (playback_location (r),
m_lvalue->playback_lvalue (),
binary_op);
}
/* Implementation of recording::memento::make_debug_string for
an assignment_op statement. */
recording::string *
recording::assignment_op::make_debug_string ()
{
return string::from_printf (m_ctxt,
"%s %s= %s;",
m_lvalue->get_debug_string (),
binary_op_strings[m_op],
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
for recording::comment. */
void
recording::comment::replay_into (replayer *r)
{
playback_block (get_block ())
->add_comment (playback_location (r),
m_text->c_str ());
}
/* Implementation of recording::memento::make_debug_string for
a comment "statement". */
recording::string *
recording::comment::make_debug_string ()
{
return string::from_printf (m_ctxt,
"/* %s */",
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
for recording::conditional. */
void
recording::conditional::replay_into (replayer *r)
{
playback_block (get_block ())
->add_conditional (playback_location (r),
m_boolval->playback_rvalue (),
playback_block (m_on_true),
playback_block (m_on_false));
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
A conditional jump has 2 successor blocks. */
vec <recording::block *>
recording::conditional::get_successor_blocks () const
{
vec <block *> result;
result.create (2);
result.quick_push (m_on_true);
result.quick_push (m_on_false);
return result;
}
/* Implementation of recording::memento::make_debug_string for
a conditional jump statement. */
recording::string *
recording::conditional::make_debug_string ()
{
if (m_on_false)
return string::from_printf (m_ctxt,
"if (%s) goto %s; else goto %s;",
m_boolval->get_debug_string (),
m_on_true->get_debug_string (),
m_on_false->get_debug_string ());
else
return string::from_printf (m_ctxt,
"if (%s) goto %s;",
m_boolval->get_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
for recording::jump. */
void
recording::jump::replay_into (replayer *r)
{
playback_block (get_block ())
->add_jump (playback_location (r),
m_target->playback_block ());
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
An unconditional jump has 1 successor block. */
vec <recording::block *>
recording::jump::get_successor_blocks () const
{
vec <block *> result;
result.create (1);
result.quick_push (m_target);
return result;
}
/* Implementation of recording::memento::make_debug_string for
a unconditional jump statement. */
recording::string *
recording::jump::make_debug_string ()
{
return string::from_printf (m_ctxt,
"goto %s;",
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
for recording::return_. */
void
recording::return_::replay_into (replayer *r)
{
playback_block (get_block ())
->add_return (playback_location (r),
m_rvalue ? m_rvalue->playback_rvalue () : NULL);
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
A return statement has no successor block. */
vec <recording::block *>
recording::return_::get_successor_blocks () const
{
vec <block *> result;
result.create (0);
return result;
}
/* Implementation of recording::memento::make_debug_string for
a return statement (covers both those with and without rvalues). */
recording::string *
recording::return_::make_debug_string ()
{
if (m_rvalue)
return string::from_printf (m_ctxt,
"return %s;",
m_rvalue->get_debug_string ());
else
return string::from_printf (m_ctxt,
"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 ()));
}
/* The implementation of class gcc::jit::recording::case_. */
void
recording::case_::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "case");
const char *fmt =
" gcc_jit_case *%s = \n"
" gcc_jit_context_new_case (%s, /*gcc_jit_context *ctxt */\n"
" %s, /* gcc_jit_rvalue *min_value */\n"
" %s, /* gcc_jit_rvalue *max_value */\n"
" %s); /* gcc_jit_block *dest_block */\n";
r.write (fmt,
id,
r.get_identifier (get_context ()),
r.get_identifier_as_rvalue (m_min_value),
r.get_identifier_as_rvalue (m_max_value),
r.get_identifier (m_dest_block));
}
recording::string *
recording::case_::make_debug_string ()
{
return string::from_printf (get_context (),
"case %s ... %s: goto %s;",
m_min_value->get_debug_string (),
m_max_value->get_debug_string (),
m_dest_block->get_debug_string ());
}
/* The implementation of class gcc::jit::recording::switch_. */
/* gcc::jit::recording::switch_'s constructor. */
recording::switch_::switch_ (block *b,
location *loc,
rvalue *expr,
block *default_block,
int num_cases,
case_ **cases)
: statement (b, loc),
m_expr (expr),
m_default_block (default_block)
{
m_cases.reserve_exact (num_cases);
for (int i = 0; i< num_cases; i++)
m_cases.quick_push (cases[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::switch_. */
void
recording::switch_::replay_into (replayer *r)
{
auto_vec <playback::case_> pcases;
int i;
recording::case_ *rcase;
pcases.reserve_exact (m_cases.length ());
FOR_EACH_VEC_ELT (m_cases, i, rcase)
{
playback::case_ pcase (rcase->get_min_value ()->playback_rvalue (),
rcase->get_max_value ()->playback_rvalue (),
rcase->get_dest_block ()->playback_block ());
pcases.safe_push (pcase);
}
playback_block (get_block ())
->add_switch (playback_location (r),
m_expr->playback_rvalue (),
m_default_block->playback_block (),
&pcases);
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
A switch statement has (NUM_CASES + 1) successor blocks. */
vec <recording::block *>
recording::switch_::get_successor_blocks () const
{
vec <block *> result;
result.create (m_cases.length () + 1);
result.quick_push (m_default_block);
int i;
case_ *c;
FOR_EACH_VEC_ELT (m_cases, i, c)
result.quick_push (c->get_dest_block ());
return result;
}
/* Implementation of recording::memento::make_debug_string for
a switch statement. */
recording::string *
recording::switch_::make_debug_string ()
{
auto_vec <char> cases_str;
int i;
case_ *c;
FOR_EACH_VEC_ELT (m_cases, i, c)
{
size_t len = strlen (c->get_debug_string ());
unsigned idx = cases_str.length ();
cases_str.safe_grow (idx + 1 + len, true);
cases_str[idx] = ' ';
memcpy (&(cases_str[idx + 1]),
c->get_debug_string (),
len);
}
cases_str.safe_push ('\0');
return string::from_printf (m_ctxt,
"switch (%s) {default: goto %s;%s}",
m_expr->get_debug_string (),
m_default_block->get_debug_string (),
&cases_str[0]);
}
/* Implementation of recording::memento::write_reproducer for
switch statements. */
void
recording::switch_::write_reproducer (reproducer &r)
{
r.make_identifier (this, "switch");
int i;
case_ *c;
const char *cases_id =
r.make_tmp_identifier ("cases_for", this);
r.write (" gcc_jit_case *%s[%i] = {\n",
cases_id,
m_cases.length ());
FOR_EACH_VEC_ELT (m_cases, i, c)
r.write (" %s,\n", r.get_identifier (c));
r.write (" };\n");
const char *fmt =
" gcc_jit_block_end_with_switch (%s, /*gcc_jit_block *block */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s, /* gcc_jit_rvalue *expr */\n"
" %s, /* gcc_jit_block *default_block */\n"
" %i, /* int num_cases */\n"
" %s); /* gcc_jit_case **cases */\n";
r.write (fmt,
r.get_identifier (get_block ()),
r.get_identifier (get_loc ()),
r.get_identifier_as_rvalue (m_expr),
r.get_identifier (m_default_block),
m_cases.length (),
cases_id);
}
/* class asm_operand : public memento. */
recording::asm_operand::asm_operand (extended_asm *ext_asm,
string *asm_symbolic_name,
string *constraint)
: memento (ext_asm->get_context ()),
m_ext_asm (ext_asm),
m_asm_symbolic_name (asm_symbolic_name),
m_constraint (constraint)
{
}
void
recording::asm_operand::print (pretty_printer *pp) const
{
if (m_asm_symbolic_name)
{
pp_character (pp, '[');
pp_string (pp, m_asm_symbolic_name->c_str ());
pp_character (pp, ']');
pp_space (pp);
}
pp_string (pp, m_constraint->get_debug_string ());
/* Subclass will add lvalue/rvalue. */
}
recording::string *
recording::asm_operand::make_debug_string ()
{
pretty_printer pp;
print (&pp);
return m_ctxt->new_string (pp_formatted_text (&pp), false);
}
/* class output_asm_operand : public asm_operand. */
void
recording::output_asm_operand::write_reproducer (reproducer &r)
{
const char *fmt =
" gcc_jit_extended_asm_add_output_operand (%s, /* gcc_jit_extended_asm *ext_asm */\n"
" %s, /* const char *asm_symbolic_name */\n"
" %s, /* const char *constraint */\n"
" %s); /* gcc_jit_lvalue *dest */\n";
r.write (fmt,
r.get_identifier (m_ext_asm),
(m_asm_symbolic_name
? m_asm_symbolic_name->get_debug_string () : "NULL"),
m_constraint->get_debug_string (),
r.get_identifier (m_dest));
}
void
recording::output_asm_operand::print (pretty_printer *pp) const
{
asm_operand::print (pp);
pp_string (pp, " (");
pp_string (pp, m_dest->get_debug_string ());
pp_string (pp, ")");
}
/* class input_asm_operand : public asm_operand. */
void
recording::input_asm_operand::write_reproducer (reproducer &r)
{
const char *fmt =
" gcc_jit_extended_asm_add_input_operand (%s, /* gcc_jit_extended_asm *ext_asm */\n"
" %s, /* const char *asm_symbolic_name */\n"
" %s, /* const char *constraint */\n"
" %s); /* gcc_jit_rvalue *src */\n";
r.write (fmt,
r.get_identifier (m_ext_asm),
(m_asm_symbolic_name
? m_asm_symbolic_name->get_debug_string () : "NULL"),
m_constraint->get_debug_string (),
r.get_identifier_as_rvalue (m_src));
}
void
recording::input_asm_operand::print (pretty_printer *pp) const
{
asm_operand::print (pp);
pp_string (pp, " (");
pp_string (pp, m_src->get_debug_string ());
pp_string (pp, ")");
}
/* The implementation of class gcc::jit::recording::extended_asm. */
void
recording::extended_asm::add_output_operand (const char *asm_symbolic_name,
const char *constraint,
lvalue *dest)
{
output_asm_operand *op
= new output_asm_operand (this,
new_string (asm_symbolic_name),
new_string (constraint),
dest);
m_ctxt->record (op);
m_output_ops.safe_push (op);
}
void
recording::extended_asm::add_input_operand (const char *asm_symbolic_name,
const char *constraint,
rvalue *src)
{
input_asm_operand *op
= new input_asm_operand (this,
new_string (asm_symbolic_name),
new_string (constraint),
src);
m_ctxt->record (op);
m_input_ops.safe_push (op);
}
void
recording::extended_asm::add_clobber (const char *victim)
{
m_clobbers.safe_push (new_string (victim));
}
/* Implementation of recording::memento::replay_into
for recording::extended_asm. */
void
recording::extended_asm::replay_into (replayer *r)
{
auto_vec<playback::asm_operand> playback_output_ops;
auto_vec<playback::asm_operand> playback_input_ops;
auto_vec<const char *> playback_clobbers;
auto_vec<playback::block *> playback_goto_blocks;
/* Populate outputs. */
{
output_asm_operand *rec_asm_op;
unsigned i;
FOR_EACH_VEC_ELT (m_output_ops, i, rec_asm_op)
{
playback::asm_operand playback_asm_op
(rec_asm_op->get_symbolic_name (),
rec_asm_op->get_constraint (),
rec_asm_op->get_lvalue ()->playback_lvalue ()->as_tree ());
playback_output_ops.safe_push (playback_asm_op);
}
}
/* Populate inputs. */
{
input_asm_operand *rec_asm_op;
unsigned i;
FOR_EACH_VEC_ELT (m_input_ops, i, rec_asm_op)
{
playback::asm_operand playback_asm_op
(rec_asm_op->get_symbolic_name (),
rec_asm_op->get_constraint (),
rec_asm_op->get_rvalue ()->playback_rvalue ()->as_tree ());
playback_input_ops.safe_push (playback_asm_op);
}
}
/* Populate clobbers. */
{
string *rec_clobber;
unsigned i;
FOR_EACH_VEC_ELT (m_clobbers, i, rec_clobber)
playback_clobbers.safe_push (rec_clobber->c_str ());
}
/* Populate playback blocks if an "asm goto". */
maybe_populate_playback_blocks (&playback_goto_blocks);
playback_block (get_block ())
->add_extended_asm (playback_location (r),
m_asm_template->c_str (),
m_is_volatile, m_is_inline,
&playback_output_ops,
&playback_input_ops,
&playback_clobbers,
&playback_goto_blocks);
}
/* Implementation of recording::memento::make_debug_string for
an extended_asm "statement". */
recording::string *
recording::extended_asm::make_debug_string ()
{
pretty_printer pp;
pp_string (&pp, "asm ");
if (m_is_volatile)
pp_string (&pp, "volatile ");
if (m_is_inline)
pp_string (&pp, "inline ");
if (is_goto ())
pp_string (&pp, "goto ");
pp_character (&pp, '(');
pp_string (&pp, m_asm_template->get_debug_string ());
pp_string (&pp, " : ");
unsigned i;
{
output_asm_operand *asm_op;
FOR_EACH_VEC_ELT (m_output_ops, i, asm_op)
{
if (i > 0)
pp_string (&pp, ", ");
asm_op->print (&pp);
}
}
pp_string (&pp, " : ");
{
input_asm_operand *asm_op;
FOR_EACH_VEC_ELT (m_input_ops, i, asm_op)
{
if (i > 0)
pp_string (&pp, ", ");
asm_op->print (&pp);
}
}
pp_string (&pp, " : ");
string *rec_clobber;
FOR_EACH_VEC_ELT (m_clobbers, i, rec_clobber)
{
if (i > 0)
pp_string (&pp, ", ");
pp_string (&pp, rec_clobber->get_debug_string ());
}
maybe_print_gotos (&pp);
pp_character (&pp, ')');
return new_string (pp_formatted_text (&pp));
}
void
recording::extended_asm::write_flags (reproducer &r)
{
if (m_is_volatile)
r.write (" gcc_jit_extended_asm_set_volatile_flag (%s, 1);\n",
r.get_identifier (this));
if (m_is_inline)
r.write (" gcc_jit_extended_asm_set_inline_flag (%s, 1);\n",
r.get_identifier (this));
}
void
recording::extended_asm::write_clobbers (reproducer &r)
{
string *clobber;
unsigned i;
FOR_EACH_VEC_ELT (m_clobbers, i, clobber)
r.write (" gcc_jit_extended_asm_add_clobber (%s, %s);\n",
r.get_identifier (this),
clobber->get_debug_string ());
}
/* Implementation of recording::memento::write_reproducer for
extended_asm_simple. */
void
recording::extended_asm_simple::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "extended_asm");
r.write (" gcc_jit_extended_asm *%s =\n"
" gcc_jit_block_add_extended_asm (%s, /*gcc_jit_block *block */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s); /* const char *asm_template */\n",
id,
r.get_identifier (get_block ()),
r.get_identifier (get_loc ()),
m_asm_template->get_debug_string ());
write_flags (r);
write_clobbers (r);
}
void
recording::extended_asm::
maybe_populate_playback_blocks (auto_vec <playback::block *> *)
{
/* Do nothing; not an "asm goto". */
}
/* The implementation of class gcc::jit::recording::extended_asm_goto. */
/* recording::extended_asm_goto's ctor. */
recording::extended_asm_goto::extended_asm_goto (block *b,
location *loc,
string *asm_template,
int num_goto_blocks,
block **goto_blocks,
block *fallthrough_block)
: extended_asm (b, loc, asm_template),
m_goto_blocks (num_goto_blocks),
m_fallthrough_block (fallthrough_block)
{
for (int i = 0; i < num_goto_blocks; i++)
m_goto_blocks.quick_push (goto_blocks[i]);
}
/* Implementation of recording::memento::replay_into
for recording::extended_asm_goto. */
void
recording::extended_asm_goto::replay_into (replayer *r)
{
/* Chain up to base class impl. */
recording::extended_asm::replay_into (r);
/* ...and potentially add a goto for the fallthrough. */
if (m_fallthrough_block)
playback_block (get_block ())
->add_jump (playback_location (r),
m_fallthrough_block->playback_block ());
}
/* Implementation of recording::memento::write_reproducer for
extended_asm_goto. */
void
recording::extended_asm_goto::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "extended_asm");
const char *blocks_id = r.make_tmp_identifier ("blocks_for", this);
r.write (" gcc_jit_block *%s[%i] = {\n",
blocks_id,
m_goto_blocks.length ());
int i;
block *b;
FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
r.write (" %s,\n", r.get_identifier (b));
r.write (" };\n");
r.write (" gcc_jit_extended_asm *%s =\n"
" gcc_jit_block_end_with_extended_asm_goto (%s, /*gcc_jit_block *block */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s, /* const char *asm_template */\n"
" %i, /* int num_goto_blocks */\n"
" %s, /* gcc_jit_block **goto_blocks */\n"
" %s); /* gcc_jit_block *fallthrough_block */\n",
id,
r.get_identifier (get_block ()),
r.get_identifier (get_loc ()),
m_asm_template->get_debug_string (),
m_goto_blocks.length (),
blocks_id,
(m_fallthrough_block
? r.get_identifier (m_fallthrough_block)
: "NULL"));
write_flags (r);
write_clobbers (r);
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
An extended_asm_goto can jump to the m_goto_blocks, and to
the (optional) m_fallthrough_block. */
vec <recording::block *>
recording::extended_asm_goto::get_successor_blocks () const
{
vec <block *> result;
result.create (m_goto_blocks.length () + 1);
if (m_fallthrough_block)
result.quick_push (m_fallthrough_block);
result.splice (m_goto_blocks);
return result;
}
/* Vfunc for use by recording::extended_asm::make_debug_string. */
void
recording::extended_asm_goto::maybe_print_gotos (pretty_printer *pp) const
{
pp_string (pp, " : ");
unsigned i;
block *b;
FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
{
if (i > 0)
pp_string (pp, ", ");
pp_string (pp, b->get_debug_string ());
}
/* Non-C syntax here. */
if (m_fallthrough_block)
pp_printf (pp, " [fallthrough: %s]",
m_fallthrough_block->get_debug_string ());
}
/* Vfunc for use by recording::extended_asm::replay_into. */
void
recording::extended_asm_goto::
maybe_populate_playback_blocks (auto_vec <playback::block *> *out)
{
unsigned i;
block *b;
FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
out->safe_push (b->playback_block ());
}
/* class top_level_asm : public memento. */
recording::top_level_asm::top_level_asm (context *ctxt,
location *loc,
string *asm_stmts)
: memento (ctxt),
m_loc (loc),
m_asm_stmts (asm_stmts)
{
}
/* Implementation of recording::memento::replay_into for top-level asm. */
void
recording::top_level_asm::replay_into (replayer *r)
{
r->add_top_level_asm (m_asm_stmts->c_str ());
}
/* Implementation of recording::memento::make_debug_string for
top-level asm. */
recording::string *
recording::top_level_asm::make_debug_string ()
{
return string::from_printf (m_ctxt, "asm (%s)",
m_asm_stmts->get_debug_string ());
}
/* Override the default implementation of
recording::memento::write_to_dump.
Don't indent the string. */
void
recording::top_level_asm::write_to_dump (dump &d)
{
d.write ("%s;\n", get_debug_string ());
}
/* Implementation of recording::memento::write_reproducer for top-level asm. */
void
recording::top_level_asm::write_reproducer (reproducer &r)
{
r.write (" gcc_jit_context_add_top_level_asm (%s, /* gcc_jit_context *ctxt */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s); /* const char *asm_stmts */\n",
r.get_identifier (get_context ()),
r.get_identifier (m_loc),
m_asm_stmts->get_debug_string ());
}
} // namespace gcc::jit
} // namespace gcc