]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/jit/jit-recording.c
Update copyright years.
[thirdparty/gcc.git] / gcc / jit / jit-recording.c
index 2900e1827464dd0d6c311134dace6ef53cd8f642..b73cd76a0a023d35baba892e728fce65aae660e4 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
@@ -23,13 +23,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "pretty-print.h"
-#include "hash-map.h"
+#include "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"
 
@@ -76,8 +74,9 @@ dump::~dump ()
 void
 dump::write (const char *fmt, ...)
 {
+  int len;
   va_list ap;
-  char *buf = NULL;
+  char *buf;
 
   /* If there was an error opening the file, we've already reported it.
      Don't attempt further work.  */
@@ -85,10 +84,10 @@ dump::write (const char *fmt, ...)
     return;
 
   va_start (ap, fmt);
-  vasprintf (&buf, fmt, ap);
+  len = vasprintf (&buf, fmt, ap);
   va_end (ap);
 
-  if (!buf)
+  if (buf == NULL || len < 0)
     {
       m_ctxt.add_error (NULL, "malloc failure writing to dumpfile %s",
                        m_filename);
@@ -237,7 +236,16 @@ class reproducer : public dump
     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;
 };
 
@@ -246,7 +254,8 @@ class reproducer : public dump
 reproducer::reproducer (recording::context &ctxt,
                        const char *filename) :
   dump (ctxt, filename, 0),
-  m_identifiers (),
+  m_map_memento_to_identifier (),
+  m_set_identifiers (),
   m_allocator ()
 {
 }
@@ -287,6 +296,35 @@ reproducer::write_args (const vec <recording::context *> &contexts)
     }
 }
 
+/* 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).
 
@@ -294,21 +332,20 @@ reproducer::write_args (const vec <recording::context *> &contexts)
 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;
 }
 
@@ -351,7 +388,7 @@ reproducer::get_identifier (recording::memento *m)
     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,
@@ -410,6 +447,62 @@ reproducer::xstrdup_printf (const char *fmt, ...)
   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.
  **********************************************************************/
@@ -459,6 +552,7 @@ 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),
@@ -487,6 +581,9 @@ recording::context::context (context *parent_ctxt)
       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
@@ -494,6 +591,7 @@ recording::context::context (context *parent_ctxt)
       memset (m_str_options, 0, sizeof (m_str_options));
       memset (m_int_options, 0, sizeof (m_int_options));
       memset (m_bool_options, 0, sizeof (m_bool_options));
+      memset (m_inner_bool_options, 0, sizeof (m_inner_bool_options));
     }
 
   memset (m_basic_types, 0, sizeof (m_basic_types));
@@ -515,6 +613,12 @@ recording::context::~context ()
   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;
 
@@ -768,6 +872,24 @@ recording::context::new_field (recording::location *loc,
   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.
 
@@ -840,7 +962,7 @@ recording::context::new_function_ptr_type (recording::location *, /* unused loc
                         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 ();
 }
 
@@ -955,6 +1077,23 @@ recording::context::new_string_literal (const char *value)
   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.
 
@@ -1074,6 +1213,22 @@ recording::context::new_array_access (recording::location *loc,
   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.
 
@@ -1092,6 +1247,7 @@ recording::context::set_str_option (enum gcc_jit_str_option opt,
     }
   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
@@ -1111,6 +1267,7 @@ recording::context::set_int_option (enum gcc_jit_int_option opt,
       return;
     }
   m_int_options[opt] = value;
+  log_int_option (opt);
 }
 
 /* Set the given boolean option for this context, or add an error if
@@ -1130,6 +1287,69 @@ recording::context::set_bool_option (enum gcc_jit_bool_option 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
@@ -1152,8 +1372,8 @@ recording::context::enable_dump (const char *dumpname,
   m_requested_dumps.safe_push (d);
 }
 
-/* Validate this context, and if it passes, compile it within a
-   mutex.
+/* 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.  */
@@ -1163,18 +1383,50 @@ recording::context::compile ()
 {
   JIT_LOG_SCOPE (get_logger ());
 
+  log_all_options ();
+
   validate ();
 
   if (errors_occurred ())
     return NULL;
 
-  /* Set up a playback context.  */
-  ::gcc::jit::playback::context replayer (this);
+  /* Set up a compile_to_memory playback context.  */
+  ::gcc::jit::playback::compile_to_memory replayer (this);
 
   /* Use it.  */
-  result *result_obj = replayer.compile ();
+  replayer.compile ();
 
-  return result_obj;
+  /* 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
@@ -1195,22 +1447,23 @@ recording::context::add_error (location *loc, const char *fmt, ...)
 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 ());
 
-  vasprintf (&malloced_msg, fmt, ap);
-  if (malloced_msg)
+  len = vasprintf (&malloced_msg, fmt, ap);
+  if (malloced_msg == NULL || len < 0)
     {
-      errmsg = malloced_msg;
-      has_ownership = true;
+      errmsg = "out of memory generating error message";
+      has_ownership = false;
     }
   else
     {
-      errmsg = "out of memory generating error message";
-      has_ownership = false;
+      errmsg = malloced_msg;
+      has_ownership = true;
     }
   if (get_logger ())
     get_logger ()->log ("error %i: %s", m_error_count, errmsg);
@@ -1349,6 +1602,92 @@ static const char * const
   "GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES"
 };
 
+static const char * const
+ inner_bool_option_reproducer_strings[NUM_INNER_BOOL_OPTIONS] = {
+  "gcc_jit_context_set_bool_allow_unreachable_blocks",
+  "gcc_jit_context_set_bool_use_external_driver"
+};
+
+/* Write the current value of all options to the log file (if any).  */
+
+void
+recording::context::log_all_options () const
+{
+  int opt_idx;
+
+  if (!get_logger ())
+    return;
+
+  for (opt_idx = 0; opt_idx < GCC_JIT_NUM_STR_OPTIONS; opt_idx++)
+    log_str_option ((enum gcc_jit_str_option)opt_idx);
+
+  for (opt_idx = 0; opt_idx < GCC_JIT_NUM_INT_OPTIONS; opt_idx++)
+    log_int_option ((enum gcc_jit_int_option)opt_idx);
+
+  for (opt_idx = 0; opt_idx < GCC_JIT_NUM_BOOL_OPTIONS; opt_idx++)
+    log_bool_option ((enum gcc_jit_bool_option)opt_idx);
+  for (opt_idx = 0; opt_idx < NUM_INNER_BOOL_OPTIONS; opt_idx++)
+    log_inner_bool_option ((enum inner_bool_option)opt_idx);
+}
+
+/* Write the current value of the given string option to the
+   log file (if any).  */
+
+void
+recording::context::log_str_option (enum gcc_jit_str_option opt) const
+{
+  gcc_assert (opt < GCC_JIT_NUM_STR_OPTIONS);
+  if (get_logger ())
+    {
+      if (m_str_options[opt])
+       log ("%s: \"%s\"",
+            str_option_reproducer_strings[opt],
+            m_str_options[opt]);
+      else
+       log ("%s: NULL",
+            str_option_reproducer_strings[opt]);
+    }
+}
+
+/* Write the current value of the given int option to the
+   log file (if any).  */
+
+void
+recording::context::log_int_option (enum gcc_jit_int_option opt) const
+{
+  gcc_assert (opt < GCC_JIT_NUM_INT_OPTIONS);
+  if (get_logger ())
+    log ("%s: %i",
+        int_option_reproducer_strings[opt],
+        m_int_options[opt]);
+}
+
+/* Write the current value of the given bool option to the
+   log file (if any).  */
+
+void
+recording::context::log_bool_option (enum gcc_jit_bool_option opt) const
+{
+  gcc_assert (opt < GCC_JIT_NUM_BOOL_OPTIONS);
+  if (get_logger ())
+    log ("%s: %s",
+        bool_option_reproducer_strings[opt],
+        m_bool_options[opt] ? "true" : "false");
+}
+
+/* Write the current value of the given "inner" bool option to the
+   log file (if any).  */
+
+void
+recording::context::log_inner_bool_option (enum inner_bool_option opt) const
+{
+  gcc_assert (opt < NUM_INNER_BOOL_OPTIONS);
+  if (get_logger ())
+    log ("%s: %s",
+        inner_bool_option_reproducer_strings[opt],
+        m_inner_bool_options[opt] ? "true" : "false");
+}
+
 /* Write C source code to PATH that attempts to replay the API
    calls made to this context (and its parents), for use in
    minimizing test cases for libgccjit.
@@ -1386,8 +1725,11 @@ recording::context::dump_reproducer_to_file (const char *path)
              == contexts[0]);
 
   r.write ("/* This code was autogenerated by"
-          " gcc_jit_context_dump_reproducer_to_file.  */\n\n");
+          " 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");
@@ -1458,12 +1800,17 @@ recording::context::dump_reproducer_to_file (const char *path)
 
       r.write ("  /* String options.  */\n");
       for (int opt_idx = 0; opt_idx < GCC_JIT_NUM_STR_OPTIONS; opt_idx++)
-       r.write ("  gcc_jit_context_set_str_option (%s,\n"
-                "                                  %s,\n"
-                "                                  \"%s\");\n",
-                r.get_identifier (contexts[ctxt_idx]),
-                str_option_reproducer_strings[opt_idx],
-                m_str_options[opt_idx] ? m_str_options[opt_idx] : "NULL");
+       {
+         r.write ("  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"
@@ -1480,6 +1827,33 @@ recording::context::dump_reproducer_to_file (const char *path)
                 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 ())
        {
@@ -1605,15 +1979,16 @@ recording::string::~string ()
 recording::string *
 recording::string::from_printf (context *ctxt, const char *fmt, ...)
 {
+  int len;
   va_list ap;
-  char *buf = NULL;
+  char *buf;
   recording::string *result;
 
   va_start (ap, fmt);
-  vasprintf (&buf, fmt, ap);
+  len = vasprintf (&buf, fmt, ap);
   va_end (ap);
 
-  if (!buf)
+  if (buf == NULL || len < 0)
     {
       ctxt->add_error (NULL, "malloc failure");
       return NULL;
@@ -1766,6 +2141,34 @@ recording::type::get_volatile ()
   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)
 {
@@ -2211,6 +2614,83 @@ recording::memento_of_get_volatile::write_reproducer (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
@@ -2291,6 +2771,53 @@ 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.  */
 
@@ -2453,7 +2980,7 @@ recording::field::replay_into (replayer *r)
    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)
@@ -2490,6 +3017,66 @@ recording::field::write_reproducer (reproducer &r)
          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.  */
@@ -2516,7 +3103,7 @@ recording::compound_type::set_fields (location *loc,
                                      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);
@@ -2711,7 +3298,7 @@ void
 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;
@@ -2807,7 +3394,7 @@ class rvalue_usage_validator : public recording::rvalue_visitor
                          recording::statement *stmt);
 
   void
-  visit (recording::rvalue *rvalue);
+  visit (recording::rvalue *rvalue) FINAL OVERRIDE;
 
  private:
   const char *m_api_funcname;
@@ -2899,19 +3486,76 @@ void
 recording::rvalue::set_scope (function *scope)
 {
   gcc_assert (scope);
-  gcc_assert (NULL == m_scope);
+  gcc_assert (m_scope == NULL);
   m_scope = scope;
 }
 
 
-/* Implementation of recording::rvalue::access_as_rvalue for rvalues
-   themselves.
-   Instances of rvalue don't need an upcast call.  */
+/* Implementation of recording::rvalue::access_as_rvalue for rvalues
+   themselves.
+   Instances of rvalue don't need an upcast call.  */
+
+const char *
+recording::rvalue::access_as_rvalue (reproducer &r)
+{
+  return r.get_identifier (this);
+}
+
+/* Return a debug string for the given rvalue, wrapping it in parentheses
+   if needed to mimic C's precedence rules, i.e. if OUTER_PREC is of
+   stronger precedence that this rvalue's precedence.
+
+   For example, given:
+
+           MULT
+          /    \
+       PLUS     MINUS
+      /    \   /     \
+     A      B C       D
+
+   we want to emit:
+
+     (A + B) * (C - D)
+
+   since MULT has strong precedence than PLUS and MINUS, whereas for:
+
+           PLUS
+          /    \
+       MULT     DIVIDE
+      /    \   /      \
+     A      B C        D
+
+   we can simply emit:
+
+     A * B + C / D
+
+   since PLUS has weaker precedence than MULT and DIVIDE.  */
 
 const char *
-recording::rvalue::access_as_rvalue (reproducer &r)
+recording::rvalue::get_debug_string_parens (enum precedence outer_prec)
 {
-  return r.get_identifier (this);
+  enum precedence this_prec = get_precedence ();
+
+  /* If this_prec has stronger precedence than outer_prec, we don't
+     need to wrap this in parens within the outer debug string.
+     Stronger precedences occur earlier than weaker within the enum,
+     so this is a less than test.  Equal precedences don't need
+     parentheses.  */
+  if (this_prec <= outer_prec)
+    return get_debug_string();
+
+  /* Otherwise, we need parentheses.  */
+
+  /* Lazily-build and cache m_parenthesized_string.  */
+  if (!m_parenthesized_string)
+    {
+      const char *debug_string = get_debug_string ();
+      m_parenthesized_string = string::from_printf (get_context (),
+                                                   "(%s)",
+                                                   debug_string);
+    }
+  gcc_assert (m_parenthesized_string);
+  return m_parenthesized_string->c_str ();
 }
 
 
@@ -3043,7 +3687,8 @@ recording::function::function (context *ctxt,
   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++)
     {
@@ -3221,7 +3866,7 @@ recording::function::validate ()
   /* Complain about empty functions with non-void return type.  */
   if (m_kind != GCC_JIT_FUNCTION_IMPORTED
       && m_return_type != m_ctxt->get_type (GCC_JIT_TYPE_VOID))
-    if (0 == m_blocks.length ())
+    if (m_blocks.length () == 0)
       m_ctxt->add_error (m_loc,
                         "function %s returns non-void (type: %s)"
                         " but has no blocks",
@@ -3240,7 +3885,9 @@ recording::function::validate ()
   }
 
   /* Check that all blocks are reachable.  */
-  if (m_blocks.length () > 0 && 0 == num_invalid_blocks)
+  if (!m_ctxt->get_inner_bool_option
+        (INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS)
+      && m_blocks.length () > 0 && num_invalid_blocks == 0)
     {
       /* Iteratively walk the graph of blocks, marking their "m_is_reachable"
         flag, starting at the initial block.  */
@@ -3253,23 +3900,13 @@ recording::function::validate ()
 
          /* 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.  */
@@ -3324,6 +3961,35 @@ recording::function::dump_to_dot (const char *path)
   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.  */
 
@@ -3517,6 +4183,30 @@ recording::block::end_with_return (recording::location *loc,
   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
@@ -3594,24 +4284,20 @@ recording::block::get_last_statement () const
     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
@@ -3689,12 +4375,14 @@ recording::block::dump_to_dot (pretty_printer *pp)
 void
 recording::block::dump_edges_to_dot (pretty_printer *pp)
 {
-  block *next[2];
-  int num_succs = get_successor_blocks (&next[0], &next[1]);
-  for (int i = 0; i < num_succs; i++)
+  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.  */
@@ -3839,6 +4527,16 @@ memento_of_new_rvalue_from_const <int>::make_debug_string ()
                              m_value);
 }
 
+/* The get_wide_int specialization for <int>.  */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <int>::get_wide_int (wide_int *out) const
+{
+  *out = wi::shwi (m_value, sizeof (m_value) * 8);
+  return true;
+}
+
 /* The write_reproducer specialization for <int>.  */
 
 template <>
@@ -3871,6 +4569,16 @@ memento_of_new_rvalue_from_const <long>::make_debug_string ()
                              m_value);
 }
 
+/* The get_wide_int specialization for <long>.  */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <long>::get_wide_int (wide_int *out) const
+{
+  *out = wi::shwi (m_value, sizeof (m_value) * 8);
+  return true;
+}
+
 /* The write_reproducer specialization for <long>.  */
 
 template <>
@@ -3895,7 +4603,7 @@ recording::memento_of_new_rvalue_from_const <long>::write_reproducer (reproducer
               id,
               r.get_identifier (get_context ()),
               r.get_identifier_as_type (m_type),
-              m_value + 1);;
+              m_value + 1);
       return;
     }
 
@@ -3924,6 +4632,15 @@ memento_of_new_rvalue_from_const <double>::make_debug_string ()
                              m_value);
 }
 
+/* The get_wide_int specialization for <double>.  */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <double>::get_wide_int (wide_int *) const
+{
+  return false;
+}
+
 /* The write_reproducer specialization for <double>.  */
 
 template <>
@@ -3963,6 +4680,15 @@ memento_of_new_rvalue_from_const <void *>::make_debug_string ()
                                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. */
 
@@ -4031,6 +4757,96 @@ recording::memento_of_new_string_literal::write_reproducer (reproducer &r)
     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
@@ -4072,7 +4888,7 @@ recording::unary_op::make_debug_string ()
                              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",
@@ -4144,14 +4960,15 @@ static const char * const binary_op_strings[] = {
 recording::string *
 recording::binary_op::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s %s %s",
-                             m_a->get_debug_string (),
+                             m_a->get_debug_string_parens (prec),
                              binary_op_strings[m_op],
-                             m_b->get_debug_string ());
+                             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",
@@ -4188,6 +5005,31 @@ recording::binary_op::write_reproducer (reproducer &r)
           r.get_identifier_as_rvalue (m_b));
 }
 
+namespace recording {
+static const enum precedence binary_op_precedence[] = {
+  PRECEDENCE_ADDITIVE, /* GCC_JIT_BINARY_OP_PLUS */
+  PRECEDENCE_ADDITIVE, /* GCC_JIT_BINARY_OP_MINUS */
+
+  PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_MULT */
+  PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_DIVIDE */
+  PRECEDENCE_MULTIPLICATIVE, /* GCC_JIT_BINARY_OP_MODULO */
+
+  PRECEDENCE_BITWISE_AND, /* GCC_JIT_BINARY_OP_BITWISE_AND */
+  PRECEDENCE_BITWISE_XOR, /* GCC_JIT_BINARY_OP_BITWISE_XOR */
+  PRECEDENCE_BITWISE_IOR, /* GCC_JIT_BINARY_OP_BITWISE_OR */
+  PRECEDENCE_LOGICAL_AND, /* GCC_JIT_BINARY_OP_LOGICAL_AND */
+  PRECEDENCE_LOGICAL_OR, /* GCC_JIT_BINARY_OP_LOGICAL_OR */
+  PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_LSHIFT */
+  PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_RSHIFT */
+};
+} /* namespace recording */
+
+enum recording::precedence
+recording::binary_op::get_precedence () const
+{
+  return binary_op_precedence[m_op];
+}
+
 /* The implementation of class gcc::jit::recording::comparison.  */
 
 /* Implementation of recording::memento::make_debug_string for
@@ -4206,11 +5048,12 @@ static const char * const comparison_strings[] =
 recording::string *
 recording::comparison::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s %s %s",
-                             m_a->get_debug_string (),
+                             m_a->get_debug_string_parens (prec),
                              comparison_strings[m_op],
-                             m_b->get_debug_string ());
+                             m_b->get_debug_string_parens (prec));
 }
 
 /* A table of enum gcc_jit_comparison values expressed in string
@@ -4268,6 +5111,25 @@ recording::comparison::visit_children (rvalue_visitor *v)
   v->visit (m_b);
 }
 
+namespace recording {
+static const enum precedence comparison_precedence[] =
+{
+  PRECEDENCE_EQUALITY, /* GCC_JIT_COMPARISON_EQ */
+  PRECEDENCE_EQUALITY, /* GCC_JIT_COMPARISON_NE */
+
+  PRECEDENCE_RELATIONAL,  /* GCC_JIT_COMPARISON_LT */
+  PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_LE */
+  PRECEDENCE_RELATIONAL,  /* GCC_JIT_COMPARISON_GT */
+  PRECEDENCE_RELATIONAL, /* GCC_JIT_COMPARISON_GE */
+};
+} /* namespace recording */
+
+enum recording::precedence
+recording::comparison::get_precedence () const
+{
+  return comparison_precedence[m_op];
+}
+
 /* Implementation of pure virtual hook recording::memento::replay_into
    for recording::cast.  */
 
@@ -4293,10 +5155,11 @@ recording::cast::visit_children (rvalue_visitor *v)
 recording::string *
 recording::cast::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "(%s)%s",
                              get_type ()->get_debug_string (),
-                             m_rvalue->get_debug_string ());
+                             m_rvalue->get_debug_string_parens (prec));
 }
 
 /* Implementation of recording::memento::write_reproducer for casts.  */
@@ -4317,6 +5180,39 @@ recording::cast::write_reproducer (reproducer &r)
           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.  */
@@ -4326,12 +5222,9 @@ recording::call::call (recording::context *ctxt,
                       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
@@ -4347,7 +5240,8 @@ recording::call::replay_into (replayer *r)
 
   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
@@ -4367,37 +5261,13 @@ recording::string *
 recording::call::make_debug_string ()
 {
   /* First, build a buffer for the arguments.  */
-  /* Calculate length of said buffer.  */
-  size_t sz = 1; /* nil terminator */
-  for (unsigned i = 0; i< m_args.length (); i++)
-    {
-      sz += strlen (m_args[i]->get_debug_string ());
-      sz += 2; /* ", " separator */
-    }
-
-  /* Now allocate and populate the buffer.  */
-  char *argbuf = new char[sz];
-  size_t len = 0;
-
-  for (unsigned i = 0; i< m_args.length (); i++)
-    {
-      strcpy (argbuf + len, m_args[i]->get_debug_string ());
-      len += strlen (m_args[i]->get_debug_string ());
-      if (i + 1 < m_args.length ())
-       {
-         strcpy (argbuf + len, ", ");
-         len += 2;
-       }
-    }
-  argbuf[len] = '\0';
+  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;
 }
@@ -4425,6 +5295,7 @@ recording::call::write_reproducer (reproducer &r)
           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.  */
@@ -4436,14 +5307,12 @@ recording::call_through_ptr::call_through_ptr (recording::context *ctxt,
                                               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
@@ -4459,7 +5328,8 @@ recording::call_through_ptr::replay_into (replayer *r)
 
   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
@@ -4479,12 +5349,13 @@ recording::call_through_ptr::visit_children (rvalue_visitor *v)
 recording::string *
 recording::call_through_ptr::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   /* First, build a buffer for the arguments.  */
   /* Calculate length of said buffer.  */
   size_t sz = 1; /* nil terminator */
   for (unsigned i = 0; i< m_args.length (); i++)
     {
-      sz += strlen (m_args[i]->get_debug_string ());
+      sz += strlen (m_args[i]->get_debug_string_parens (prec));
       sz += 2; /* ", " separator */
     }
 
@@ -4494,8 +5365,8 @@ recording::call_through_ptr::make_debug_string ()
 
   for (unsigned i = 0; i< m_args.length (); i++)
     {
-      strcpy (argbuf + len, m_args[i]->get_debug_string ());
-      len += strlen (m_args[i]->get_debug_string ());
+      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, ", ");
@@ -4507,7 +5378,7 @@ recording::call_through_ptr::make_debug_string ()
   /* ...and use it to get the string for the call as a whole.  */
   string *result = string::from_printf (m_ctxt,
                                        "%s (%s)",
-                                       m_fn_ptr->get_debug_string (),
+                                       m_fn_ptr->get_debug_string_parens (prec),
                                        argbuf);
 
   delete[] argbuf;
@@ -4541,6 +5412,7 @@ recording::call_through_ptr::write_reproducer (reproducer &r)
           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.  */
@@ -4573,10 +5445,11 @@ recording::array_access::visit_children (rvalue_visitor *v)
 recording::string *
 recording::array_access::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s[%s]",
-                             m_ptr->get_debug_string (),
-                             m_index->get_debug_string ());
+                             m_ptr->get_debug_string_parens (prec),
+                             m_index->get_debug_string_parens (prec));
 }
 
 /* Implementation of recording::memento::write_reproducer for
@@ -4628,9 +5501,10 @@ recording::access_field_of_lvalue::visit_children (rvalue_visitor *v)
 recording::string *
 recording::access_field_of_lvalue::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s.%s",
-                             m_lvalue->get_debug_string (),
+                             m_lvalue->get_debug_string_parens (prec),
                              m_field->get_debug_string ());
 }
 
@@ -4680,9 +5554,10 @@ recording::access_field_rvalue::visit_children (rvalue_visitor *v)
 recording::string *
 recording::access_field_rvalue::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s.%s",
-                             m_rvalue->get_debug_string (),
+                             m_rvalue->get_debug_string_parens (prec),
                              m_field->get_debug_string ());
 }
 
@@ -4733,9 +5608,10 @@ recording::dereference_field_rvalue::visit_children (rvalue_visitor *v)
 recording::string *
 recording::dereference_field_rvalue::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "%s->%s",
-                             m_rvalue->get_debug_string (),
+                             m_rvalue->get_debug_string_parens (prec),
                              m_field->get_debug_string ());
 }
 
@@ -4784,9 +5660,10 @@ recording::dereference_rvalue::visit_children (rvalue_visitor *v)
 recording::string *
 recording::dereference_rvalue::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "*%s",
-                             m_rvalue->get_debug_string ());
+                             m_rvalue->get_debug_string_parens (prec));
 }
 
 /* Implementation of recording::memento::write_reproducer for
@@ -4832,9 +5709,10 @@ recording::get_address_of_lvalue::visit_children (rvalue_visitor *v)
 recording::string *
 recording::get_address_of_lvalue::make_debug_string ()
 {
+  enum precedence prec = get_precedence ();
   return string::from_printf (m_ctxt,
                              "&%s",
-                             m_lvalue->get_debug_string ());
+                             m_lvalue->get_debug_string_parens (prec));
 }
 
 /* Implementation of recording::memento::write_reproducer for
@@ -4852,6 +5730,51 @@ recording::get_address_of_lvalue::write_reproducer (reproducer &r)
           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
@@ -4906,14 +5829,15 @@ recording::local::write_reproducer (reproducer &r)
    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
@@ -5122,13 +6046,14 @@ recording::conditional::replay_into (replayer *r)
 
    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
@@ -5186,12 +6111,13 @@ recording::jump::replay_into (replayer *r)
 
    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
@@ -5237,11 +6163,12 @@ recording::return_::replay_into (replayer *r)
 
    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
@@ -5279,6 +6206,158 @@ recording::return_::write_reproducer (reproducer &r)
             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