Implementation of the gcc::jit::recording internal API, so that libgccjit.c can record the calls that are made to the public API, for later playback by the dummy frontend.
gcc/jit/ * jit-recording.c: New. --- gcc/jit/jit-recording.c | 3434 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3434 insertions(+) create mode 100644 gcc/jit/jit-recording.c diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c new file mode 100644 index 0000000..8daa8f2 --- /dev/null +++ b/gcc/jit/jit-recording.c @@ -0,0 +1,3434 @@ +/* Internals of libgccjit: classes for recording calls made to the JIT API. + Copyright (C) 2013-2014 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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 "opts.h" +#include "tree.h" +#include "pretty-print.h" + +#include <pthread.h> + +#include "jit-common.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, ...) +{ + va_list ap; + char *buf = NULL; + + /* 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); + vasprintf (&buf, fmt, ap); + va_end (ap); + + if (!buf) + { + 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); + + /* 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); +} + +/********************************************************************** + 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) + : m_parent_ctxt (parent_ctxt), + m_error_count (0), + m_first_error_str (NULL), + m_owns_first_error_str (false), + m_mementos (), + m_compound_types (), + m_functions (), + m_FILE_type (NULL), + m_builtins_manager(NULL) +{ + if (parent_ctxt) + { + /* Inherit options from parent. + Note that the first memcpy means copying pointers to strings. */ + memcpy (m_str_options, + parent_ctxt->m_str_options, + sizeof (m_str_options)); + 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)); + } + 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_basic_types, 0, sizeof (m_basic_types)); +} + +/* The destructor for gcc::jit::recording::context, implicitly used by + gcc_jit_context_release. */ + +recording::context::~context () +{ + int i; + memento *m; + FOR_EACH_VEC_ELT (m_mementos, i, m) + { + delete m; + } + + if (m_builtins_manager) + delete m_builtins_manager; + + if (m_owns_first_error_str) + free (m_first_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) +{ + 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 () +{ + 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) +{ + if (!text) + return NULL; + + recording::string *result = new string (this, text); + 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) +{ + recording::location *result = + new recording::location (this, + new_string (filename), + line, column); + 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::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::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 (this, + return_type, + num_params, + param_types, + is_variadic); + record (fn_type); + + /* Return a pointer-type to the 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; +} + +/* 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) +{ + if (!m_builtins_manager) + m_builtins_manager = new builtins_manager (this); + return m_builtins_manager->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, + recording::type *type, + const char *name) +{ + recording::lvalue *result = + new recording::global (this, loc, type, new_string (name)); + record (result); + return result; +} + +/* Create a recording::memento_of_new_rvalue_from_int instance and add + it to this context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_rvalue_from_int. */ + +recording::rvalue * +recording::context::new_rvalue_from_int (recording::type *type, + int value) +{ + recording::rvalue *result = + new memento_of_new_rvalue_from_int (this, NULL, type, value); + record (result); + return result; +} + +/* Create a recording::memento_of_new_rvalue_from_double instance and + add it to this context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_rvalue_from_double. */ + +recording::rvalue * +recording::context::new_rvalue_from_double (recording::type *type, + double value) +{ + recording::rvalue *result = + new memento_of_new_rvalue_from_double (this, NULL, type, value); + record (result); + return result; +} + +/* Create a recording::memento_of_new_rvalue_from_ptr instance and add + it to this context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_rvalue_from_ptr. */ + +recording::rvalue * +recording::context::new_rvalue_from_ptr (recording::type *type, + void *value) +{ + recording::rvalue *result = + new memento_of_new_rvalue_from_ptr (this, NULL, type, value); + record (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::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; +} + +/* 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; + } + m_str_options[opt] = value; +} + +/* 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; +} + +/* 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; +} + +/* This mutex guards gcc::jit::recording::context::compile, so that only + one thread can be accessing the bulk of GCC's state at once. */ + +static pthread_mutex_t jit_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Validate this context, and if it passes, compile it within a + mutex. + + Implements the post-error-checking part of + gcc_jit_context_compile. */ + +result * +recording::context::compile () +{ + validate (); + + if (errors_occurred ()) + return NULL; + + /* Acquire the big GCC mutex. */ + pthread_mutex_lock (&jit_mutex); + gcc_assert (NULL == ::gcc::jit::active_playback_ctxt); + + /* Set up a playback context. */ + ::gcc::jit::playback::context replayer (this); + ::gcc::jit::active_playback_ctxt = &replayer; + + result *result_obj = replayer.compile (); + + /* Release the big GCC mutex. */ + ::gcc::jit::active_playback_ctxt = NULL; + pthread_mutex_unlock (&jit_mutex); + + return result_obj; +} + +/* 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) +{ + char *malloced_msg; + const char *errmsg; + bool has_ownership; + + vasprintf (&malloced_msg, fmt, ap); + if (malloced_msg) + { + errmsg = malloced_msg; + has_ownership = true; + } + else + { + errmsg = "out of memory generating error message"; + has_ownership = false; + } + + 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; + } + else + if (has_ownership) + free (malloced_msg); + + 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; +} + +/* 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"); + } + + function *fn; + FOR_EACH_VEC_ELT (m_functions, i, fn) + { + fn->write_to_dump (d); + } +} + +/* 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 () +{ + 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) + : memento (ctxt) +{ + 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, ...) +{ + va_list ap; + char *buf = NULL; + recording::string *result; + + va_start (ap, fmt); + vasprintf (&buf, fmt, ap); + va_end (ap); + + if (!buf) + { + 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 () +{ + /* Hack to avoid infinite recursion into strings when logging all + mementos: don't re-escape strings: */ + if (m_buffer[0] == '"') + 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]; + if (ch == '\t' || ch == '\n' || ch == '\\' || ch == '"') + APPEND('\\'); + APPEND(ch); + } + APPEND('"'); /* closing quote */ +#undef APPEND + tmp[len] = '\0'; /* nil termintator */ + + string *result = m_ctxt->new_string (tmp); + + delete[] tmp; + return result; +} + +/* 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); +} + +/* 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; +} + +/* 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: + /* 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; + } +} + +/* 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; + } +} + +/* 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; + } +} + +/* 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 */ + +}; + +/* 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]); +} + +/* The implementation of class gcc::jit::recording::memento_of_get_pointer. */ + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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); +} + +/* 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 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. */ + 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 (), + ¶m_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; +} + +/* 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-byfield. */ + +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; +} + +/* 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 (NULL == m_fields); + + 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 */)); +} + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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 *) +{ + 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::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; +} + +/* 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; +} + +/* 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 ())); +} + + +/* 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 () +{ + for (int i = 0; i< num_params; i++) + m_params.safe_push (params[i]); +} + +/* 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. */ + 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 (), + ¶ms, + 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 (0 == m_blocks.length ()) + 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_blocks.length () > 0 && 0 == num_invalid_blocks) + { + /* Iteratively walk the graph of blocks, marking their "m_is_reachable" + flag, starting at the initial block. */ + vec<block *> worklist; + worklist.create (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 . */ + block *next1, *next2; + int n = b->get_successor_blocks (&next1, &next2); + switch (n) + { + default: + gcc_unreachable (); + case 2: + if (!next2->m_is_reachable) + worklist.safe_push (next2); + /* fallthrough */ + case 1: + if (!next1->m_is_reachable) + worklist.safe_push (next1); + break; + case 0: + break; + } + } + + /* 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 {\n", get_debug_string ()); + + /* 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_printf (pp, "}\n"); + pp_flush (pp); + fclose (fp); +} + +/* Implementation of recording::memento::make_debug_string for + functions. */ + +recording::string * +recording::function::make_debug_string () +{ + return m_name; +} + +/* 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 post-error-checking part of + gcc_jit_block_add_eval. */ + +void +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); +} + +/* 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 post-error-checking part of + gcc_jit_block_add_assignment. */ + +void +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); +} + +/* 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 post-error-checking part of + gcc_jit_block_add_assignment_op. */ + +void +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); +} + +/* 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 post-error-checking part of + gcc_jit_block_add_comment. */ + +void +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); +} + +/* 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 post-error-checking part of + gcc_jit_block_end_with_conditional. */ + +void +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; +} + +/* 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 post-error-checking part of + gcc_jit_block_end_with_jump. */ + +void +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; +} + +/* 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. */ + +void +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; +} + +/* 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 () +{ + 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 number of + successor blocks, which will be 0, 1 or 2, for return, unconditional + jump, and conditional jump respectively. + NEXT1 and NEXT2 must be non-NULL. The first successor block (if any) + is written to NEXT1, and the second (if any) to NEXT2. + + Used when validating functions, and when dumping dot representations + of them. */ + +int +recording::block::get_successor_blocks (block **next1, block **next2) const +{ + gcc_assert (m_has_been_terminated); + gcc_assert (next1); + gcc_assert (next2); + statement *last_statement = get_last_statement (); + gcc_assert (last_statement); + return last_statement->get_successor_blocks (next1, next2); +} + +/* 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); +} + +/* 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_printf (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) +{ + block *next[2]; + int num_succs = get_successor_blocks (&next[0], &next[1]); + for (int i = 0; i < num_succs; i++) + pp_printf (pp, + "\tblock_%d:s -> block_%d:n;\n", + m_index, next[i]->m_index); +} + +/* 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 (r->new_global (playback_location (r, m_loc), + m_type->playback_type (), + playback_string (m_name))); +} + +/* The implementation of class gcc::jit::recording::memento_of_new_rvalue_from_int. */ + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::memento_of_new_rvalue_from_int. */ + +void +recording::memento_of_new_rvalue_from_int::replay_into (replayer *r) +{ + set_playback_obj (r->new_rvalue_from_int (m_type->playback_type (), + m_value)); +} + +/* Implementation of recording::memento::make_debug_string for + rvalue_from_int, rendering it as + (TYPE)LITERAL + e.g. + "(int)42". */ + +recording::string * +recording::memento_of_new_rvalue_from_int::make_debug_string () +{ + return string::from_printf (m_ctxt, + "(%s)%i", + m_type->get_debug_string (), + m_value); +} + +/* The implementation of class gcc::jit::recording::memento_of_new_rvalue_from_double. */ + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::memento_of_new_rvalue_from_double. */ + +void +recording::memento_of_new_rvalue_from_double::replay_into (replayer *r) +{ + set_playback_obj (r->new_rvalue_from_double (m_type->playback_type (), + m_value)); +} + +/* Implementation of recording::memento::make_debug_string for + rvalue_from_double, rendering it as + (TYPE)LITERAL + e.g. + "(float)42.0". */ + +recording::string * +recording::memento_of_new_rvalue_from_double::make_debug_string () +{ + return string::from_printf (m_ctxt, + "(%s)%f", + m_type->get_debug_string (), + m_value); +} + +/* The implementation of class gcc::jit::recording::memento_of_new_rvalue_from_ptr. */ + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::memento_of_new_rvalue_from_ptr. */ + +void +recording::memento_of_new_rvalue_from_ptr::replay_into (replayer *r) +{ + set_playback_obj (r->new_rvalue_from_ptr (m_type->playback_type (), + m_value)); +} + +/* Implementation of recording::memento::make_debug_string for + rvalue_from_ptr, rendering it as + (TYPE)HEX + e.g. + "(int *)0xdeadbeef" + + Zero is rendered as NULL e.g. + "(int *)NULL". */ + +recording::string * +recording::memento_of_new_rvalue_from_ptr::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 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, + m_value->get_debug_string ()); +} + +/* 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 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 */ +}; + +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 ()); +} + +/* 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 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 () +{ + return string::from_printf (m_ctxt, + "%s %s %s", + m_a->get_debug_string (), + binary_op_strings[m_op], + m_b->get_debug_string ()); +} + +/* 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 () +{ + return string::from_printf (m_ctxt, + "%s %s %s", + m_a->get_debug_string (), + comparison_strings[m_op], + m_b->get_debug_string ()); +} + +/* 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::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 recording::memento::make_debug_string for + casts. */ + +recording::string * +recording::cast::make_debug_string () +{ + return string::from_printf (m_ctxt, + "(%s)%s", + get_type ()->get_debug_string (), + m_rvalue->get_debug_string ()); +} + +/* 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) +: rvalue (ctxt, loc, func->get_return_type ()), + m_func (func), + m_args () +{ + for (int i = 0; i< numargs; i++) + m_args.safe_push (args[i]); +} + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::call. */ + +void +recording::call::replay_into (replayer *r) +{ + 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)); +} + +/* Implementation of recording::memento::make_debug_string for + function calls. */ + +recording::string * +recording::call::make_debug_string () +{ + /* 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 ()); + 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 ()); + len += strlen (m_args[i]->get_debug_string ()); + 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_func->get_debug_string (), + argbuf); + + delete[] argbuf; + + return result; +} + +/* 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) +: rvalue (ctxt, loc, + fn_ptr->get_type ()->dereference () + ->as_a_function_type ()->get_return_type ()), + m_fn_ptr (fn_ptr), + m_args () +{ + for (int i = 0; i< numargs; i++) + m_args.safe_push (args[i]); +} + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::call_through_ptr. */ + +void +recording::call_through_ptr::replay_into (replayer *r) +{ + 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)); +} + +/* Implementation of recording::memento::make_debug_string for + calls through function ptrs. */ + +recording::string * +recording::call_through_ptr::make_debug_string () +{ + /* 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 ()); + 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 ()); + len += strlen (m_args[i]->get_debug_string ()); + 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 (), + argbuf); + + delete[] argbuf; + + return result; +} + +/* 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 recording::memento::make_debug_string for + array accesses. */ + +recording::string * +recording::array_access::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s[%s]", + m_ptr->get_debug_string (), + m_index->get_debug_string ()); +} + +/* 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 recording::memento::make_debug_string for + accessing a field of an lvalue. */ + +recording::string * +recording::access_field_of_lvalue::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s.%s", + m_lvalue->get_debug_string (), + m_field->get_debug_string ()); +} + +/* 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 recording::memento::make_debug_string for + accessing a field of an rvalue. */ + +recording::string * +recording::access_field_rvalue::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s.%s", + m_rvalue->get_debug_string (), + m_field->get_debug_string ()); +} + +/* 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 recording::memento::make_debug_string for + dereferencing a field of an rvalue. */ + +recording::string * +recording::dereference_field_rvalue::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s->%s", + m_rvalue->get_debug_string (), + m_field->get_debug_string ()); +} + +/* 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 recording::memento::make_debug_string for + dereferencing an rvalue. */ + +recording::string * +recording::dereference_rvalue::make_debug_string () +{ + return string::from_printf (m_ctxt, + "*%s", + m_rvalue->get_debug_string ()); +} + +/* 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 recording::memento::make_debug_string for + getting the address of an lvalue. */ + +recording::string * +recording::get_address_of_lvalue::make_debug_string () +{ + return string::from_printf (m_ctxt, + "&%s", + m_lvalue->get_debug_string ()); +} + +/* 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 ()); +} + +/* 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. */ + +int +recording::statement::get_successor_blocks (block **/*out_next1*/, + block **/*out_next2*/) const +{ + /* The base class implementation is for non-terminating statements, + and thus should never be called. */ + gcc_unreachable (); + return 0; +} + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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 ()); +} + +/* 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. */ + +int +recording::conditional::get_successor_blocks (block **out_next1, + block **out_next2) const +{ + *out_next1 = m_on_true; + *out_next2 = m_on_false; + return 2; +} + +/* 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 ()); +} + +/* 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. */ + +int +recording::jump::get_successor_blocks (block **out_next1, + block **/*out_next2*/) const +{ + *out_next1 = m_target; + return 1; +} + +/* 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 ()); +} + +/* 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. */ + +int +recording::return_::get_successor_blocks (block **/*out_next1*/, + block **/*out_next2*/) const +{ + return 0; +} + +/* 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;"); +} + +} // namespace gcc::jit + +} // namespace gcc -- 1.8.5.3