| /* Internals of libgccjit: classes for recording calls made to the JIT API. |
| Copyright (C) 2013-2022 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 "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 < ARRAY_SIZE (m_str_options); 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)); |
| m_inner_bool_options[INNER_BOOL_OPTION_PRINT_ERRORS_TO_STDERR] = true; |
| } |
| |
| 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.cc'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); |
| if (num_bits == 128) |
| return get_type (is_signed |
| ? GCC_JIT_TYPE_INT128_T |
| : GCC_JIT_TYPE_UINT128_T); |
| |
| /* 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; |
| } |
| |
| void |
| recording::context::new_global_init_rvalue (lvalue *variable, |
| rvalue *init) |
| { |
| recording::global_init_rvalue *obj = |
| new recording::global_init_rvalue (this, variable, init); |
| record (obj); |
| |
| global *gbl = (global *) variable; |
| gbl->set_rvalue_init (init); /* Needed by the global for write dump. */ |
| } |
| |
| /* 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; |
| } |
| |
| recording::rvalue * |
| recording::context::new_ctor (recording::location *loc, |
| recording::type *type, |
| size_t num_values, |
| field **fields, |
| rvalue **values) |
| { |
| recording::ctor *result = new ctor (this, loc, type); |
| |
| /* Short cut for zero init. */ |
| if (!num_values) |
| { |
| record (result); |
| return result; |
| } |
| |
| bool is_struct_or_union = type->is_struct () || type->is_union (); |
| |
| /* We need to copy fields and values into result's auto_vec:s. |
| Both for structs and unions and only values for arrays. */ |
| if (type->is_array () != NULL) |
| { |
| result->m_values.reserve (num_values, false); |
| |
| for (size_t i = 0; i < num_values; i++) |
| result->m_values.quick_push (values[i]); |
| } |
| else if (is_struct_or_union && fields) |
| { |
| /* ctor values are paired with user specified fields. */ |
| |
| result->m_values.reserve (num_values, false); |
| result->m_fields.reserve (num_values, false); |
| |
| for (size_t i = 0; i < num_values; i++) |
| { |
| result->m_values.quick_push (values[i]); |
| result->m_fields.quick_push (fields[i]); |
| } |
| } |
| else if (is_struct_or_union && !fields) |
| { |
| /* ctor values are in definition order one by one, |
| so take the fields from the type object. */ |
| |
| result->m_values.reserve (num_values, false); |
| result->m_fields.reserve (num_values, false); |
| |
| compound_type *ct = reinterpret_cast<compound_type *>(type); |
| recording::fields *fields = ct->get_fields (); |
| |
| /* The entry point checks that num_values is not greater than |
| the amount of fields in 'fields'. */ |
| for (size_t i = 0; i < num_values; i++) |
| { |
| result->m_values.quick_push (values[i]); |
| result->m_fields.quick_push (fields->get_field (i)); |
| } |
| } |
| else |
| gcc_unreachable (); |
| |
| 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::bitcast instance and add it to this context's list |
| of mementos. |
| |
| Implements the post-error-checking part of |
| gcc_jit_context_new_bitcast. */ |
| |
| recording::rvalue * |
| recording::context::new_bitcast (location *loc, |
| rvalue *expr, |
| type *type_) |
| { |
| recording::rvalue *result = new bitcast (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"; |
| |
| bool print_errors_to_stderr = |
| get_inner_bool_option (INNER_BOOL_OPTION_PRINT_ERRORS_TO_STDERR); |
| if (print_errors_to_stderr) |
| { |
| 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", |
| "gcc_jit_context_set_bool_print_errors_to_stderr", |
| }; |
| |
| /* 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_UINT8_T: |
| case GCC_JIT_TYPE_INT8_T: |
| size = 8; |
| break; |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_INT16_T: |
| size = 16; |
| break; |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_INT32_T: |
| size = 32; |
| break; |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_INT64_T: |
| size = 64; |
| break; |
| case GCC_JIT_TYPE_UINT128_T: |
| case GCC_JIT_TYPE_INT128_T: |
| size = 128; |
| 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; |
| case GCC_JIT_TYPE_SIZE_T: |
| size = MAX_BITS_PER_WORD; |
| 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_UINT8_T: |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_UINT128_T: |
| case GCC_JIT_TYPE_INT8_T: |
| case GCC_JIT_TYPE_INT16_T: |
| case GCC_JIT_TYPE_INT32_T: |
| case GCC_JIT_TYPE_INT64_T: |
| case GCC_JIT_TYPE_INT128_T: |
| 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: |
| case GCC_JIT_TYPE_UINT8_T: |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_UINT128_T: |
| case GCC_JIT_TYPE_INT8_T: |
| case GCC_JIT_TYPE_INT16_T: |
| case GCC_JIT_TYPE_INT32_T: |
| case GCC_JIT_TYPE_INT64_T: |
| case GCC_JIT_TYPE_INT128_T: |
| 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_signed for |
| recording::memento_of_get_type. */ |
| |
| bool |
| recording::memento_of_get_type::is_signed () const |
| { |
| switch (m_kind) |
| { |
| default: gcc_unreachable (); |
| |
| case GCC_JIT_TYPE_SIGNED_CHAR: |
| case GCC_JIT_TYPE_CHAR: |
| case GCC_JIT_TYPE_SHORT: |
| case GCC_JIT_TYPE_INT: |
| case GCC_JIT_TYPE_LONG: |
| case GCC_JIT_TYPE_LONG_LONG: |
| case GCC_JIT_TYPE_INT8_T: |
| case GCC_JIT_TYPE_INT16_T: |
| case GCC_JIT_TYPE_INT32_T: |
| case GCC_JIT_TYPE_INT64_T: |
| case GCC_JIT_TYPE_INT128_T: |
| return true; |
| |
| case GCC_JIT_TYPE_VOID: |
| case GCC_JIT_TYPE_VOID_PTR: |
| case GCC_JIT_TYPE_BOOL: |
| case GCC_JIT_TYPE_UNSIGNED_CHAR: |
| case GCC_JIT_TYPE_UNSIGNED_SHORT: |
| case GCC_JIT_TYPE_UNSIGNED_INT: |
| case GCC_JIT_TYPE_UNSIGNED_LONG: |
| case GCC_JIT_TYPE_UNSIGNED_LONG_LONG: |
| case GCC_JIT_TYPE_UINT8_T: |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_UINT128_T: |
| |
| case GCC_JIT_TYPE_FLOAT: |
| case GCC_JIT_TYPE_DOUBLE: |
| case GCC_JIT_TYPE_LONG_DOUBLE: |
| |
| case GCC_JIT_TYPE_CONST_CHAR_PTR: |
| |
| case GCC_JIT_TYPE_SIZE_T: |
| |
| case GCC_JIT_TYPE_FILE_PTR: |
| |
| 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: |
| case GCC_JIT_TYPE_UINT8_T: |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_UINT128_T: |
| case GCC_JIT_TYPE_INT8_T: |
| case GCC_JIT_TYPE_INT16_T: |
| case GCC_JIT_TYPE_INT32_T: |
| case GCC_JIT_TYPE_INT64_T: |
| case GCC_JIT_TYPE_INT128_T: |
| 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: |
| case GCC_JIT_TYPE_UINT8_T: |
| case GCC_JIT_TYPE_UINT16_T: |
| case GCC_JIT_TYPE_UINT32_T: |
| case GCC_JIT_TYPE_UINT64_T: |
| case GCC_JIT_TYPE_UINT128_T: |
| case GCC_JIT_TYPE_INT8_T: |
| case GCC_JIT_TYPE_INT16_T: |
| case GCC_JIT_TYPE_INT32_T: |
| case GCC_JIT_TYPE_INT64_T: |
| case GCC_JIT_TYPE_INT128_T: |
| 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 */ |
| |
| "__uint8_t", /* GCC_JIT_TYPE_UINT8_T */ |
| "__uint16_t", /* GCC_JIT_TYPE_UINT16_T */ |
| "__uint32_t", /* GCC_JIT_TYPE_UINT32_T */ |
| "__uint64_t", /* GCC_JIT_TYPE_UINT64_T */ |
| "__uint128_t", /* GCC_JIT_TYPE_UINT128_T */ |
| "__int8_t", /* GCC_JIT_TYPE_INT8_T */ |
| "__int16_t", /* GCC_JIT_TYPE_INT16_T */ |
| "__int32_t", /* GCC_JIT_TYPE_INT32_T */ |
| "__int64_t", /* GCC_JIT_TYPE_INT64_T */ |
| "__int128_t", /* GCC_JIT_TYPE_INT128_T */ |
| |
| }; |
| |
| /* 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", |
| "GCC_JIT_TYPE_UINT8_T", |
| "GCC_JIT_TYPE_UINT16_T", |
| "GCC_JIT_TYPE_UINT32_T", |
| "GCC_JIT_TYPE_UINT64_T", |
| "GCC_JIT_TYPE_UINT128_T", |
| "GCC_JIT_TYPE_INT8_T", |
| "GCC_JIT_TYPE_INT16_T", |
| "GCC_JIT_TYPE_INT32_T", |
| "GCC_JIT_TYPE_INT64_T", |
| "GCC_JIT_TYPE_INT128_T", |
| }; |
| |
| 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 *). */ |
| if (m_other_type->unqualified ()->accepts_writes_from (rtype_points_to)) |
| { |
| return true; |
| } |
| |
| /* It's OK to assign to a (volatile const T *) from a (volatile const T *). */ |
| return m_other_type->is_same_type_as (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 (), |
| ¶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; |
| } |
| |
| /* 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) |
| { |
| <