/* Internals of libgccjit: classes for recording calls made to the JIT API.
- Copyright (C) 2013-2015 Free Software Foundation, Inc.
+ Copyright (C) 2013-2020 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "pretty-print.h"
-#include "hash-map.h"
#include "toplev.h"
#include <pthread.h>
-#include "jit-common.h"
#include "jit-builtins.h"
-#include "jit-logging.h"
#include "jit-recording.h"
#include "jit-playback.h"
GNU_PRINTF(2, 3);
private:
- hash_map<recording::memento *, const char *> m_identifiers;
+ 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;
};
reproducer::reproducer (recording::context &ctxt,
const char *filename) :
dump (ctxt, filename, 0),
- m_identifiers (),
+ m_map_memento_to_identifier (),
+ m_set_identifiers (),
m_allocator ()
{
}
}
}
+/* 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).
const char *
reproducer::make_identifier (recording::memento *m, const char *prefix)
{
- char *result;
+ const char *result;
if (strlen (m->get_debug_string ()) < 100)
{
- result = m_allocator.xstrdup_printf ("%s_%s_%p",
- prefix,
- m->get_debug_string (),
- (void *) m);
- for (char *p = result; *p; p++)
- if (!ISALNUM (*p))
- *p = '_';
+ 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);
- m_identifiers.put (m, result);
+ result = ensure_identifier_is_unique (result, m);
+ m_map_memento_to_identifier.put (m, result);
return result;
}
if (!loc->created_by_user ())
return "NULL";
- const char **slot = m_identifiers.get (m);
+ const char **slot = m_map_memento_to_identifier.get (m);
if (!slot)
{
get_context ().add_error (NULL,
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.
**********************************************************************/
: 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),
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;
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.
param_types,
is_variadic);
- /* Return a pointer-type to the the function type. */
+ /* Return a pointer-type to the function type. */
return fn_type->get_pointer ();
}
return result;
}
+/* Create a recording::memento_of_new_rvalue_from_vector instance and add it
+ to this context's list of mementos.
+
+ Implements the post-error-checking part of
+ gcc_jit_context_new_rvalue_from_vector. */
+
+recording::rvalue *
+recording::context::new_rvalue_from_vector (location *loc,
+ vector_type *type,
+ rvalue **elements)
+{
+ recording::rvalue *result
+ = new memento_of_new_rvalue_from_vector (this, loc, type, elements);
+ record (result);
+ return result;
+}
+
/* Create a recording::unary_op instance and add it to this context's
list of mementos.
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.
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.
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_allow_unreachable_blocks",
+ "gcc_jit_context_set_bool_use_external_driver"
};
/* Write the current value of all options to the log file (if any). */
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");
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)
{
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
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. */
recording::memento::write_to_dump. Dump each field
by dumping a line of the form:
TYPE NAME;
- so that we can build up a struct/union field-byfield. */
+ so that we can build up a struct/union field by field. */
void
recording::field::write_to_dump (dump &d)
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. */
field **field_array)
{
m_loc = loc;
- gcc_assert (NULL == m_fields);
+ gcc_assert (m_fields == NULL);
m_fields = new fields (this, num_fields, field_array);
m_ctxt->record (m_fields);
recording::fields::write_reproducer (reproducer &r)
{
if (m_struct_or_union)
- if (NULL == m_struct_or_union->dyn_cast_struct ())
+ if (m_struct_or_union->dyn_cast_struct () == NULL)
/* We have a union; the fields have already been written by
union::write_reproducer. */
return;
recording::statement *stmt);
void
- visit (recording::rvalue *rvalue);
+ visit (recording::rvalue *rvalue) FINAL OVERRIDE;
private:
const char *m_api_funcname;
recording::rvalue::set_scope (function *scope)
{
gcc_assert (scope);
- gcc_assert (NULL == m_scope);
+ gcc_assert (m_scope == NULL);
m_scope = scope;
}
m_is_variadic (is_variadic),
m_builtin_id (builtin_id),
m_locals (),
- m_blocks ()
+ m_blocks (),
+ m_fn_ptr_type (NULL)
{
for (int i = 0; i< num_params; i++)
{
/* Complain about empty functions with non-void return type. */
if (m_kind != GCC_JIT_FUNCTION_IMPORTED
&& m_return_type != m_ctxt->get_type (GCC_JIT_TYPE_VOID))
- if (0 == m_blocks.length ())
+ if (m_blocks.length () == 0)
m_ctxt->add_error (m_loc,
"function %s returns non-void (type: %s)"
" but has no blocks",
/* Check that all blocks are reachable. */
if (!m_ctxt->get_inner_bool_option
(INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS)
- && m_blocks.length () > 0 && 0 == num_invalid_blocks)
+ && m_blocks.length () > 0 && num_invalid_blocks == 0)
{
/* Iteratively walk the graph of blocks, marking their "m_is_reachable"
flag, starting at the initial block. */
/* Add successor blocks that aren't yet marked to the worklist. */
/* We checked that each block has a terminating statement above . */
- block *next1, *next2;
- int n = b->get_successor_blocks (&next1, &next2);
- switch (n)
- {
- default:
- gcc_unreachable ();
- case 2:
- if (!next2->m_is_reachable)
- worklist.safe_push (next2);
- /* fallthrough */
- case 1:
- if (!next1->m_is_reachable)
- worklist.safe_push (next1);
- break;
- case 0:
- break;
- }
+ vec <block *> successors = b->get_successor_blocks ();
+ int i;
+ block *succ;
+ FOR_EACH_VEC_ELT (successors, i, succ)
+ if (!succ->m_is_reachable)
+ worklist.safe_push (succ);
+ successors.release ();
}
/* Now complain about any blocks that haven't been marked. */
fclose (fp);
}
+/* Implements the post-error-checking part of
+ gcc_jit_function_get_address. */
+
+recording::rvalue *
+recording::function::get_address (recording::location *loc)
+{
+ /* Lazily create and cache the function pointer type. */
+ if (!m_fn_ptr_type)
+ {
+ /* Make a recording::function_type for this function. */
+ auto_vec <recording::type *> param_types (m_params.length ());
+ unsigned i;
+ recording::param *param;
+ FOR_EACH_VEC_ELT (m_params, i, param)
+ param_types.safe_push (param->get_type ());
+ recording::function_type *fn_type
+ = m_ctxt->new_function_type (m_return_type,
+ m_params.length (),
+ param_types.address (),
+ m_is_variadic);
+ m_fn_ptr_type = fn_type->get_pointer ();
+ }
+ gcc_assert (m_fn_ptr_type);
+
+ rvalue *result = new function_pointer (get_context (), loc, this, m_fn_ptr_type);
+ m_ctxt->record (result);
+ return result;
+}
+
/* Implementation of recording::memento::make_debug_string for
functions. */
return result;
}
+/* Create a recording::switch_ instance and add it to
+ the block's context's list of mementos, and to the block's
+ list of statements.
+
+ Implements the heart of gcc_jit_block_end_with_switch. */
+
+recording::statement *
+recording::block::end_with_switch (recording::location *loc,
+ recording::rvalue *expr,
+ recording::block *default_block,
+ int num_cases,
+ recording::case_ **cases)
+{
+ statement *result = new switch_ (this, loc,
+ expr,
+ default_block,
+ num_cases,
+ cases);
+ m_ctxt->record (result);
+ m_statements.safe_push (result);
+ m_has_been_terminated = true;
+ return result;
+}
+
/* Override the default implementation of
recording::memento::write_to_dump for blocks by writing
an unindented block name as a label, followed by the indented
return NULL;
}
-/* Assuming that this block has been terminated, get the number of
- successor blocks, which will be 0, 1 or 2, for return, unconditional
- jump, and conditional jump respectively.
- NEXT1 and NEXT2 must be non-NULL. The first successor block (if any)
- is written to NEXT1, and the second (if any) to NEXT2.
+/* Assuming that this block has been terminated, get the successor blocks
+ as a vector. Ownership of the vector transfers to the caller, which
+ must call its release () method.
Used when validating functions, and when dumping dot representations
of them. */
-int
-recording::block::get_successor_blocks (block **next1, block **next2) const
+vec <recording::block *>
+recording::block::get_successor_blocks () const
{
gcc_assert (m_has_been_terminated);
- gcc_assert (next1);
- gcc_assert (next2);
statement *last_statement = get_last_statement ();
gcc_assert (last_statement);
- return last_statement->get_successor_blocks (next1, next2);
+ return last_statement->get_successor_blocks ();
}
/* Implementation of pure virtual hook recording::memento::replay_into
void
recording::block::dump_edges_to_dot (pretty_printer *pp)
{
- block *next[2];
- int num_succs = get_successor_blocks (&next[0], &next[1]);
- for (int i = 0; i < num_succs; i++)
+ vec <block *> successors = get_successor_blocks ();
+ int i;
+ block *succ;
+ FOR_EACH_VEC_ELT (successors, i, succ)
pp_printf (pp,
"\tblock_%d:s -> block_%d:n;\n",
- m_index, next[i]->m_index);
+ m_index, succ->m_index);
+ successors.release ();
}
/* The implementation of class gcc::jit::recording::global. */
m_value);
}
+/* The get_wide_int specialization for <int>. */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <int>::get_wide_int (wide_int *out) const
+{
+ *out = wi::shwi (m_value, sizeof (m_value) * 8);
+ return true;
+}
+
/* The write_reproducer specialization for <int>. */
template <>
m_value);
}
+/* The get_wide_int specialization for <long>. */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <long>::get_wide_int (wide_int *out) const
+{
+ *out = wi::shwi (m_value, sizeof (m_value) * 8);
+ return true;
+}
+
/* The write_reproducer specialization for <long>. */
template <>
id,
r.get_identifier (get_context ()),
r.get_identifier_as_type (m_type),
- m_value + 1);;
+ m_value + 1);
return;
}
m_value);
}
+/* The get_wide_int specialization for <double>. */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <double>::get_wide_int (wide_int *) const
+{
+ return false;
+}
+
/* The write_reproducer specialization for <double>. */
template <>
m_type->get_debug_string ());
}
+/* The get_wide_int specialization for <void *>. */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <void *>::get_wide_int (wide_int *) const
+{
+ return false;
+}
+
/* Implementation of recording::memento::write_reproducer for <void *>
values. */
m_value->get_debug_string ());
}
+/* The implementation of class
+ gcc::jit::recording::memento_of_new_rvalue_from_vector. */
+
+/* The constructor for
+ gcc::jit::recording::memento_of_new_rvalue_from_vector. */
+
+recording::memento_of_new_rvalue_from_vector::
+memento_of_new_rvalue_from_vector (context *ctxt,
+ location *loc,
+ vector_type *type,
+ rvalue **elements)
+: rvalue (ctxt, loc, type),
+ m_vector_type (type),
+ m_elements ()
+{
+ for (unsigned i = 0; i < type->get_num_units (); i++)
+ m_elements.safe_push (elements[i]);
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+ for recording::memento_of_new_rvalue_from_vector. */
+
+void
+recording::memento_of_new_rvalue_from_vector::replay_into (replayer *r)
+{
+ auto_vec<playback::rvalue *> playback_elements;
+ playback_elements.create (m_elements.length ());
+ for (unsigned i = 0; i< m_elements.length (); i++)
+ playback_elements.safe_push (m_elements[i]->playback_rvalue ());
+
+ set_playback_obj (r->new_rvalue_from_vector (playback_location (r, m_loc),
+ m_type->playback_type (),
+ playback_elements));
+}
+
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+ for recording::memento_of_new_rvalue_from_vector. */
+
+void
+recording::memento_of_new_rvalue_from_vector::visit_children (rvalue_visitor *v)
+{
+ for (unsigned i = 0; i< m_elements.length (); i++)
+ v->visit (m_elements[i]);
+}
+
+/* Implementation of recording::memento::make_debug_string for
+ vectors. */
+
+recording::string *
+recording::memento_of_new_rvalue_from_vector::make_debug_string ()
+{
+ comma_separated_string elements (m_elements, get_precedence ());
+
+ /* Now build a string. */
+ string *result = string::from_printf (m_ctxt,
+ "{%s}",
+ elements.as_char_ptr ());
+
+ return result;
+
+}
+
+/* Implementation of recording::memento::write_reproducer for
+ vectors. */
+
+void
+recording::memento_of_new_rvalue_from_vector::write_reproducer (reproducer &r)
+{
+ const char *id = r.make_identifier (this, "vector");
+ const char *elements_id = r.make_tmp_identifier ("elements_for_", this);
+ r.write (" gcc_jit_rvalue *%s[%i] = {\n",
+ elements_id,
+ m_elements.length ());
+ for (unsigned i = 0; i< m_elements.length (); i++)
+ r.write (" %s,\n", r.get_identifier_as_rvalue (m_elements[i]));
+ r.write (" };\n");
+ r.write (" gcc_jit_rvalue *%s =\n"
+ " gcc_jit_context_new_rvalue_from_vector (%s, /* gcc_jit_context *ctxt */\n"
+ " %s, /* gcc_jit_location *loc */\n"
+ " %s, /* gcc_jit_type *vec_type */\n"
+ " %i, /* size_t num_elements */ \n"
+ " %s); /* gcc_jit_rvalue **elements*/\n",
+ id,
+ r.get_identifier (get_context ()),
+ r.get_identifier (m_loc),
+ r.get_identifier (m_vector_type),
+ m_elements.length (),
+ elements_id);
+}
+
/* The implementation of class gcc::jit::recording::unary_op. */
/* Implementation of pure virtual hook recording::memento::replay_into
m_a->get_debug_string ());
}
-static const char * const unary_op_reproducer_strings[] = {
+const char * const unary_op_reproducer_strings[] = {
"GCC_JIT_UNARY_OP_MINUS",
"GCC_JIT_UNARY_OP_BITWISE_NEGATE",
"GCC_JIT_UNARY_OP_LOGICAL_NEGATE",
m_b->get_debug_string_parens (prec));
}
-static const char * const binary_op_reproducer_strings[] = {
+const char * const binary_op_reproducer_strings[] = {
"GCC_JIT_BINARY_OP_PLUS",
"GCC_JIT_BINARY_OP_MINUS",
"GCC_JIT_BINARY_OP_MULT",
r.get_identifier_as_type (get_type ()));
}
+/* The implementation of class gcc::jit::recording::base_call. */
+
+/* The constructor for gcc::jit::recording::base_call. */
+
+recording::base_call::base_call (context *ctxt,
+ location *loc,
+ type *type_,
+ int numargs,
+ rvalue **args)
+: rvalue (ctxt, loc, type_),
+ m_args (),
+ m_require_tail_call (0)
+{
+ for (int i = 0; i< numargs; i++)
+ m_args.safe_push (args[i]);
+}
+
+/* Subroutine for use by call and call_though_ptr's write_reproducer
+ methods. */
+
+void
+recording::base_call::write_reproducer_tail_call (reproducer &r,
+ const char *id)
+{
+ if (m_require_tail_call)
+ {
+ r.write (" gcc_jit_rvalue_set_bool_require_tail_call (%s, /* gcc_jit_rvalue *call*/\n"
+ " %i); /* int require_tail_call*/\n",
+ id,
+ 1);
+ }
+}
+
/* The implementation of class gcc::jit::recording::call. */
/* The constructor for gcc::jit::recording::call. */
recording::function *func,
int numargs,
rvalue **args)
-: rvalue (ctxt, loc, func->get_return_type ()),
- m_func (func),
- m_args ()
+: base_call (ctxt, loc, func->get_return_type (), numargs, args),
+ m_func (func)
{
- for (int i = 0; i< numargs; i++)
- m_args.safe_push (args[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
set_playback_obj (r->new_call (playback_location (r, m_loc),
m_func->playback_function (),
- &playback_args));
+ &playback_args,
+ m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
recording::string *
recording::call::make_debug_string ()
{
- enum precedence prec = get_precedence ();
/* First, build a buffer for the arguments. */
- /* Calculate length of said buffer. */
- size_t sz = 1; /* nil terminator */
- for (unsigned i = 0; i< m_args.length (); i++)
- {
- sz += strlen (m_args[i]->get_debug_string_parens (prec));
- sz += 2; /* ", " separator */
- }
-
- /* Now allocate and populate the buffer. */
- char *argbuf = new char[sz];
- size_t len = 0;
-
- for (unsigned i = 0; i< m_args.length (); i++)
- {
- strcpy (argbuf + len, m_args[i]->get_debug_string_parens (prec));
- len += strlen (m_args[i]->get_debug_string_parens (prec));
- if (i + 1 < m_args.length ())
- {
- strcpy (argbuf + len, ", ");
- len += 2;
- }
- }
- argbuf[len] = '\0';
+ comma_separated_string args (m_args, get_precedence ());
/* ...and use it to get the string for the call as a whole. */
string *result = string::from_printf (m_ctxt,
"%s (%s)",
m_func->get_debug_string (),
- argbuf);
-
- delete[] argbuf;
+ args.as_char_ptr ());
return result;
}
r.get_identifier (m_func),
m_args.length (),
args_id);
+ write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::call_through_ptr. */
recording::rvalue *fn_ptr,
int numargs,
rvalue **args)
-: rvalue (ctxt, loc,
- fn_ptr->get_type ()->dereference ()
- ->as_a_function_type ()->get_return_type ()),
- m_fn_ptr (fn_ptr),
- m_args ()
+: base_call (ctxt, loc,
+ fn_ptr->get_type ()->dereference ()
+ ->as_a_function_type ()->get_return_type (),
+ numargs, args),
+ m_fn_ptr (fn_ptr)
{
- for (int i = 0; i< numargs; i++)
- m_args.safe_push (args[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
m_fn_ptr->playback_rvalue (),
- &playback_args));
+ &playback_args,
+ m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
r.get_identifier_as_rvalue (m_fn_ptr),
m_args.length (),
args_id);
+ write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::array_access. */
r.get_identifier (m_loc));
}
+/* The implementation of class gcc::jit::recording::function_pointer. */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+ for recording::function_pointer. */
+
+void
+recording::function_pointer::replay_into (replayer *r)
+{
+ set_playback_obj (
+ m_fn->playback_function ()->
+ get_address (playback_location (r, m_loc)));
+}
+
+void
+recording::function_pointer::visit_children (rvalue_visitor *)
+{
+ /* Empty. */
+}
+
+/* Implementation of recording::memento::make_debug_string for
+ getting the address of an lvalue. */
+
+recording::string *
+recording::function_pointer::make_debug_string ()
+{
+ return string::from_printf (m_ctxt,
+ "%s",
+ m_fn->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+ function_pointer. */
+
+void
+recording::function_pointer::write_reproducer (reproducer &r)
+{
+ const char *id = r.make_identifier (this, "address_of");
+ r.write (" gcc_jit_rvalue *%s =\n"
+ " gcc_jit_function_get_address (%s, /* gcc_jit_function *fn */\n"
+ " %s); /* gcc_jit_location *loc */\n",
+ id,
+ r.get_identifier (m_fn),
+ r.get_identifier (m_loc));
+}
+
/* The implementation of class gcc::jit::recording::local. */
/* Implementation of pure virtual hook recording::memento::replay_into
since this vfunc must only ever be called on terminator
statements. */
-int
-recording::statement::get_successor_blocks (block **/*out_next1*/,
- block **/*out_next2*/) const
+vec <recording::block *>
+recording::statement::get_successor_blocks () const
{
/* The base class implementation is for non-terminating statements,
and thus should never be called. */
gcc_unreachable ();
- return 0;
+ vec <block *> result;
+ result.create (0);
+ return result;
}
/* Extend the default implementation of
A conditional jump has 2 successor blocks. */
-int
-recording::conditional::get_successor_blocks (block **out_next1,
- block **out_next2) const
+vec <recording::block *>
+recording::conditional::get_successor_blocks () const
{
- *out_next1 = m_on_true;
- *out_next2 = m_on_false;
- return 2;
+ vec <block *> result;
+ result.create (2);
+ result.quick_push (m_on_true);
+ result.quick_push (m_on_false);
+ return result;
}
/* Implementation of recording::memento::make_debug_string for
An unconditional jump has 1 successor block. */
-int
-recording::jump::get_successor_blocks (block **out_next1,
- block **/*out_next2*/) const
+vec <recording::block *>
+recording::jump::get_successor_blocks () const
{
- *out_next1 = m_target;
- return 1;
+ vec <block *> result;
+ result.create (1);
+ result.quick_push (m_target);
+ return result;
}
/* Implementation of recording::memento::make_debug_string for
A return statement has no successor block. */
-int
-recording::return_::get_successor_blocks (block **/*out_next1*/,
- block **/*out_next2*/) const
+vec <recording::block *>
+recording::return_::get_successor_blocks () const
{
- return 0;
+ vec <block *> result;
+ result.create (0);
+ return result;
}
/* Implementation of recording::memento::make_debug_string for
r.get_identifier (get_loc ()));
}
+/* The implementation of class gcc::jit::recording::case_. */
+
+void
+recording::case_::write_reproducer (reproducer &r)
+{
+ const char *id = r.make_identifier (this, "case");
+ const char *fmt =
+ " gcc_jit_case *%s = \n"
+ " gcc_jit_context_new_case (%s, /*gcc_jit_context *ctxt */\n"
+ " %s, /* gcc_jit_rvalue *min_value */\n"
+ " %s, /* gcc_jit_rvalue *max_value */\n"
+ " %s); /* gcc_jit_block *dest_block */\n";
+ r.write (fmt,
+ id,
+ r.get_identifier (get_context ()),
+ r.get_identifier_as_rvalue (m_min_value),
+ r.get_identifier_as_rvalue (m_max_value),
+ r.get_identifier (m_dest_block));
+}
+
+recording::string *
+recording::case_::make_debug_string ()
+{
+ return string::from_printf (get_context (),
+ "case %s ... %s: goto %s;",
+ m_min_value->get_debug_string (),
+ m_max_value->get_debug_string (),
+ m_dest_block->get_debug_string ());
+}
+
+/* The implementation of class gcc::jit::recording::switch_. */
+
+/* gcc::jit::recording::switch_'s constructor. */
+
+recording::switch_::switch_ (block *b,
+ location *loc,
+ rvalue *expr,
+ block *default_block,
+ int num_cases,
+ case_ **cases)
+: statement (b, loc),
+ m_expr (expr),
+ m_default_block (default_block)
+{
+ m_cases.reserve_exact (num_cases);
+ for (int i = 0; i< num_cases; i++)
+ m_cases.quick_push (cases[i]);
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+ for recording::switch_. */
+
+void
+recording::switch_::replay_into (replayer *r)
+{
+ auto_vec <playback::case_> pcases;
+ int i;
+ recording::case_ *rcase;
+ pcases.reserve_exact (m_cases.length ());
+ FOR_EACH_VEC_ELT (m_cases, i, rcase)
+ {
+ playback::case_ pcase (rcase->get_min_value ()->playback_rvalue (),
+ rcase->get_max_value ()->playback_rvalue (),
+ rcase->get_dest_block ()->playback_block ());
+ pcases.safe_push (pcase);
+ }
+ playback_block (get_block ())
+ ->add_switch (playback_location (r),
+ m_expr->playback_rvalue (),
+ m_default_block->playback_block (),
+ &pcases);
+}
+
+/* Override the poisoned default implementation of
+ gcc::jit::recording::statement::get_successor_blocks
+
+ A switch statement has (NUM_CASES + 1) successor blocks. */
+
+vec <recording::block *>
+recording::switch_::get_successor_blocks () const
+{
+ vec <block *> result;
+ result.create (m_cases.length () + 1);
+ result.quick_push (m_default_block);
+ int i;
+ case_ *c;
+ FOR_EACH_VEC_ELT (m_cases, i, c)
+ result.quick_push (c->get_dest_block ());
+ return result;
+}
+
+/* Implementation of recording::memento::make_debug_string for
+ a switch statement. */
+
+recording::string *
+recording::switch_::make_debug_string ()
+{
+ auto_vec <char> cases_str;
+ int i;
+ case_ *c;
+ FOR_EACH_VEC_ELT (m_cases, i, c)
+ {
+ size_t len = strlen (c->get_debug_string ());
+ unsigned idx = cases_str.length ();
+ cases_str.safe_grow (idx + 1 + len);
+ cases_str[idx] = ' ';
+ memcpy (&(cases_str[idx + 1]),
+ c->get_debug_string (),
+ len);
+ }
+ cases_str.safe_push ('\0');
+
+ return string::from_printf (m_ctxt,
+ "switch (%s) {default: goto %s;%s}",
+ m_expr->get_debug_string (),
+ m_default_block->get_debug_string (),
+ &cases_str[0]);
+}
+
+/* Implementation of recording::memento::write_reproducer for
+ switch statements. */
+
+void
+recording::switch_::write_reproducer (reproducer &r)
+{
+ r.make_identifier (this, "switch");
+ int i;
+ case_ *c;
+ const char *cases_id =
+ r.make_tmp_identifier ("cases_for", this);
+ r.write (" gcc_jit_case *%s[%i] = {\n",
+ cases_id,
+ m_cases.length ());
+ FOR_EACH_VEC_ELT (m_cases, i, c)
+ r.write (" %s,\n", r.get_identifier (c));
+ r.write (" };\n");
+ const char *fmt =
+ " gcc_jit_block_end_with_switch (%s, /*gcc_jit_block *block */\n"
+ " %s, /* gcc_jit_location *loc */\n"
+ " %s, /* gcc_jit_rvalue *expr */\n"
+ " %s, /* gcc_jit_block *default_block */\n"
+ " %i, /* int num_cases */\n"
+ " %s); /* gcc_jit_case **cases */\n";
+ r.write (fmt,
+ r.get_identifier (get_block ()),
+ r.get_identifier (get_loc ()),
+ r.get_identifier_as_rvalue (m_expr),
+ r.get_identifier (m_default_block),
+ m_cases.length (),
+ cases_id);
+}
+
} // namespace gcc::jit
} // namespace gcc