This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 16/27] New file: gcc/jit/jit-playback.c


This files implements the gcc::jit::playback internal API, called by
the dummy "frontend" to replay the public API calls made to the
library.  A thin wrapper around trees.

gcc/jit/
	* jit-playback.c: New.
---
 gcc/jit/jit-playback.c | 2104 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2104 insertions(+)
 create mode 100644 gcc/jit/jit-playback.c

diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
new file mode 100644
index 0000000..dc1b468
--- /dev/null
+++ b/gcc/jit/jit-playback.c
@@ -0,0 +1,2104 @@
+/* Internals of libgccjit: classes for playing back recorded API calls.
+   Copyright (C) 2013-2014 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "opts.h"
+#include "tree.h"
+#include "cgraph.h"
+#include "toplev.h"
+#include "timevar.h"
+#include "tree-cfg.h"
+#include "target.h"
+#include "convert.h"
+#include "stringpool.h"
+#include "stor-layout.h"
+#include "print-tree.h"
+#include "gimplify.h"
+#include "gcc-driver-name.h"
+
+#include "jit-common.h"
+#include "jit-playback.h"
+
+
+/* gcc::jit::playback::context::build_cast uses the convert.h API,
+   which in turn requires the frontend to provide a "convert"
+   function, apparently as a fallback.
+
+   Hence we provide this dummy one, with the requirement that any casts
+   are handled before reaching this.  */
+extern tree convert (tree type, tree expr);
+
+tree
+convert (tree dst_type, tree expr)
+{
+  gcc_assert (gcc::jit::active_playback_ctxt);
+  gcc::jit::active_playback_ctxt->add_error (NULL, "unhandled conversion");
+  fprintf (stderr, "input expression:\n");
+  debug_tree (expr);
+  fprintf (stderr, "requested type:\n");
+  debug_tree (dst_type);
+  return error_mark_node;
+}
+
+namespace gcc {
+namespace jit {
+
+/**********************************************************************
+ Playback.
+ **********************************************************************/
+
+/* The constructor for gcc::jit::playback::context.  */
+
+playback::context::context (recording::context *ctxt)
+  : m_recording_ctxt (ctxt),
+    m_char_array_type_node (NULL),
+    m_const_char_ptr (NULL)
+{
+  m_functions.create (0);
+  m_source_files.create (0);
+  m_cached_locations.create (0);
+}
+
+/* The destructor for gcc::jit::playback::context.  */
+
+playback::context::~context ()
+{
+  if (get_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES))
+    fprintf (stderr, "intermediate files written to %s\n", m_path_tempdir);
+  else
+    {
+      /* Clean up .s/.so and tempdir. */
+      if (m_path_s_file)
+        unlink (m_path_s_file);
+      if (m_path_so_file)
+        unlink (m_path_so_file);
+      if (m_path_tempdir)
+        rmdir (m_path_tempdir);
+    }
+
+  free (m_path_template);
+  /* m_path_tempdir aliases m_path_template, or is NULL, so don't
+     attempt to free it .  */
+  free (m_path_c_file);
+  free (m_path_s_file);
+  free (m_path_so_file);
+  m_functions.release ();
+}
+
+/* A playback::context can reference GC-managed pointers.  Mark them
+   ("by hand", rather than by gengtype).
+
+   This is called on the active playback context (if any) by the
+   my_ggc_walker hook in the jit_root_table in dummy-frontend.c.  */
+
+void
+playback::context::
+gt_ggc_mx ()
+{
+  int i;
+  function *func;
+  FOR_EACH_VEC_ELT (m_functions, i, func)
+    {
+      if (ggc_test_and_set_mark (func))
+	func->gt_ggc_mx ();
+    }
+}
+
+/* Given an enum gcc_jit_types value, get a "tree" type.  */
+
+static tree
+get_tree_node_for_type (enum gcc_jit_types type_)
+{
+  switch (type_)
+    {
+    case GCC_JIT_TYPE_VOID:
+      return void_type_node;
+
+    case GCC_JIT_TYPE_VOID_PTR:
+      return ptr_type_node;
+
+    case GCC_JIT_TYPE_BOOL:
+      return boolean_type_node;
+
+    case GCC_JIT_TYPE_CHAR:
+      return char_type_node;
+    case GCC_JIT_TYPE_SIGNED_CHAR:
+      return signed_char_type_node;
+    case GCC_JIT_TYPE_UNSIGNED_CHAR:
+      return unsigned_char_type_node;
+
+    case GCC_JIT_TYPE_SHORT:
+      return short_integer_type_node;
+    case GCC_JIT_TYPE_UNSIGNED_SHORT:
+      return short_unsigned_type_node;
+
+    case GCC_JIT_TYPE_CONST_CHAR_PTR:
+      {
+	tree const_char = build_qualified_type (char_type_node,
+						TYPE_QUAL_CONST);
+	return build_pointer_type (const_char);
+      }
+
+    case GCC_JIT_TYPE_INT:
+      return integer_type_node;
+    case GCC_JIT_TYPE_UNSIGNED_INT:
+      return unsigned_type_node;
+
+    case GCC_JIT_TYPE_LONG:
+      return long_integer_type_node;
+    case GCC_JIT_TYPE_UNSIGNED_LONG:
+      return long_unsigned_type_node;
+
+    case GCC_JIT_TYPE_LONG_LONG:
+      return long_long_integer_type_node;
+    case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
+      return long_long_unsigned_type_node;
+
+    case GCC_JIT_TYPE_FLOAT:
+      return float_type_node;
+    case GCC_JIT_TYPE_DOUBLE:
+      return double_type_node;
+    case GCC_JIT_TYPE_LONG_DOUBLE:
+      return long_double_type_node;
+
+    case GCC_JIT_TYPE_SIZE_T:
+      return size_type_node;
+
+    case GCC_JIT_TYPE_FILE_PTR:
+      return fileptr_type_node;
+    }
+
+  return NULL;
+}
+
+/* Construct a playback::type instance (wrapping a tree) for the given
+   enum value.  */
+
+playback::type *
+playback::context::
+get_type (enum gcc_jit_types type_)
+{
+  tree type_node = get_tree_node_for_type (type_);
+  if (NULL == type_node)
+    {
+      add_error (NULL,
+		 "unrecognized (enum gcc_jit_types) value: %i", type_);
+      return NULL;
+    }
+
+  return new type (type_node);
+}
+
+/* Construct a playback::type instance (wrapping a tree) for the given
+   array type.  */
+
+playback::type *
+playback::context::
+new_array_type (playback::location *loc,
+		playback::type *element_type,
+		int num_elements)
+{
+  gcc_assert (element_type);
+
+  tree t = build_array_type_nelts (element_type->as_tree (),
+				   num_elements);
+  layout_type (t);
+
+  if (loc)
+    set_tree_location (t, loc);
+
+  return new type (t);
+}
+
+/* Construct a playback::field instance (wrapping a tree).  */
+
+playback::field *
+playback::context::
+new_field (location *loc,
+	   type *type,
+	   const char *name)
+{
+  gcc_assert (type);
+  gcc_assert (name);
+
+  /* compare with c/c-decl.c:grokfield and grokdeclarator.  */
+  tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+			  get_identifier (name), type->as_tree ());
+
+  if (loc)
+    set_tree_location (decl, loc);
+
+  return new field (decl);
+}
+
+/* Construct a playback::compound_type instance (wrapping a tree).  */
+
+playback::compound_type *
+playback::context::
+new_compound_type (location *loc,
+		   const char *name,
+		   bool is_struct) /* else is union */
+{
+  gcc_assert (name);
+
+  /* Compare with c/c-decl.c: start_struct. */
+
+  tree t = make_node (is_struct ? RECORD_TYPE : UNION_TYPE);
+  TYPE_NAME (t) = get_identifier (name);
+  TYPE_SIZE (t) = 0;
+
+  if (loc)
+    set_tree_location (t, loc);
+
+  return new compound_type (t);
+}
+
+void
+playback::compound_type::set_fields (const vec<playback::field *> &fields)
+{
+  /* Compare with c/c-decl.c: finish_struct. */
+  tree t = as_tree ();
+
+  tree fieldlist = NULL;
+  for (unsigned i = 0; i < fields.length (); i++)
+    {
+      field *f = fields[i];
+      DECL_CONTEXT (f->as_tree ()) = t;
+      fieldlist = chainon (f->as_tree (), fieldlist);
+    }
+  fieldlist = nreverse (fieldlist);
+  TYPE_FIELDS (t) = fieldlist;
+
+  layout_type (t);
+}
+
+/* Construct a playback::type instance (wrapping a tree) for a function
+   type.  */
+
+playback::type *
+playback::context::
+new_function_type (type *return_type,
+		   vec<type *> *param_types,
+		   int is_variadic)
+{
+  int i;
+  type *param_type;
+
+  tree *arg_types = (tree *)xcalloc(param_types->length (), sizeof(tree*));
+
+  FOR_EACH_VEC_ELT (*param_types, i, param_type)
+    arg_types[i] = param_type->as_tree ();
+
+  tree fn_type;
+  if (is_variadic)
+    fn_type =
+      build_varargs_function_type_array (return_type->as_tree (),
+					 param_types->length (),
+					 arg_types);
+  else
+    fn_type = build_function_type_array (return_type->as_tree (),
+					 param_types->length (),
+					 arg_types);
+  free (arg_types);
+
+  return new type (fn_type);
+}
+
+/* Construct a playback::param instance (wrapping a tree).  */
+
+playback::param *
+playback::context::
+new_param (location *loc,
+	   type *type,
+	   const char *name)
+{
+  gcc_assert (type);
+  gcc_assert (name);
+  tree inner = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			   get_identifier (name), type->as_tree ());
+  if (loc)
+    set_tree_location (inner, loc);
+
+  return new param (this, inner);
+}
+
+/* Construct a playback::function instance.  */
+
+playback::function *
+playback::context::
+new_function (location *loc,
+	      enum gcc_jit_function_kind kind,
+	      type *return_type,
+	      const char *name,
+	      vec<param *> *params,
+	      int is_variadic,
+	      enum built_in_function builtin_id)
+{
+  int i;
+  param *param;
+
+  //can return_type be NULL?
+  gcc_assert (name);
+
+  tree *arg_types = (tree *)xcalloc(params->length (), sizeof(tree*));
+  FOR_EACH_VEC_ELT (*params, i, param)
+    arg_types[i] = TREE_TYPE (param->as_tree ());
+
+  tree fn_type;
+  if (is_variadic)
+    fn_type = build_varargs_function_type_array (return_type->as_tree (),
+						 params->length (), arg_types);
+  else
+    fn_type = build_function_type_array (return_type->as_tree (),
+					 params->length (), arg_types);
+  free (arg_types);
+
+  /* FIXME: this uses input_location: */
+  tree fndecl = build_fn_decl (name, fn_type);
+
+  if (loc)
+    set_tree_location (fndecl, loc);
+
+  tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL,
+			     NULL_TREE, return_type->as_tree ());
+  DECL_ARTIFICIAL (resdecl) = 1;
+  DECL_IGNORED_P (resdecl) = 1;
+  DECL_RESULT (fndecl) = resdecl;
+
+  if (builtin_id)
+    {
+      DECL_BUILT_IN_CLASS (fndecl) = BUILT_IN_NORMAL;
+      DECL_FUNCTION_CODE (fndecl) = builtin_id;
+      gcc_assert (loc == NULL);
+      DECL_SOURCE_LOCATION (fndecl) = BUILTINS_LOCATION;
+    }
+
+  if (kind != GCC_JIT_FUNCTION_IMPORTED)
+    {
+      tree param_decl_list = NULL;
+      FOR_EACH_VEC_ELT (*params, i, param)
+	{
+	  param_decl_list = chainon (param->as_tree (), param_decl_list);
+	}
+
+      /* The param list was created in reverse order; fix it: */
+      param_decl_list = nreverse (param_decl_list);
+
+      tree t;
+      for (t = param_decl_list; t; t = DECL_CHAIN (t))
+	{
+	  DECL_CONTEXT (t) = fndecl;
+	  DECL_ARG_TYPE (t) = TREE_TYPE (t);
+	}
+
+      /* Set it up on DECL_ARGUMENTS */
+      DECL_ARGUMENTS(fndecl) = param_decl_list;
+    }
+
+  if (kind == GCC_JIT_FUNCTION_ALWAYS_INLINE)
+    {
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+      /* Add attribute "always_inline": */
+      DECL_ATTRIBUTES (fndecl) =
+	tree_cons (get_identifier ("always_inline"),
+		   NULL,
+		   DECL_ATTRIBUTES (fndecl));
+    }
+
+  function *func = new function (this, fndecl, kind);
+  m_functions.safe_push (func);
+  return func;
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree).  */
+
+playback::lvalue *
+playback::context::
+new_global (location *loc,
+            type *type,
+            const char *name)
+{
+  gcc_assert (type);
+  gcc_assert (name);
+  tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			   get_identifier (name),
+			   type->as_tree ());
+  TREE_PUBLIC (inner) = 1;
+  DECL_COMMON (inner) = 1;
+  DECL_EXTERNAL (inner) = 1;
+
+  if (loc)
+    set_tree_location (inner, loc);
+
+  return new lvalue (this, inner);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree).  */
+
+playback::rvalue *
+playback::context::
+new_rvalue_from_int (type *type,
+		     int value)
+{
+  // FIXME: type-checking, or coercion?
+  tree inner_type = type->as_tree ();
+  if (INTEGRAL_TYPE_P (inner_type))
+    {
+      tree inner = build_int_cst (inner_type, value);
+      return new rvalue (this, inner);
+    }
+  else
+    {
+      REAL_VALUE_TYPE real_value;
+      real_from_integer (&real_value, VOIDmode, value, SIGNED);
+      tree inner = build_real (inner_type, real_value);
+      return new rvalue (this, inner);
+    }
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree).  */
+
+playback::rvalue *
+playback::context::
+new_rvalue_from_double (type *type,
+			double value)
+{
+  // FIXME: type-checking, or coercion?
+  tree inner_type = type->as_tree ();
+
+  /* We have a "double", we want a REAL_VALUE_TYPE.
+
+     real.c:real_from_target appears to require the representation to be
+     split into 32-bit values, and then sent as an pair of host long
+     ints.  */
+  REAL_VALUE_TYPE real_value;
+  union
+  {
+    double as_double;
+    uint32_t as_uint32s[2];
+  } u;
+  u.as_double = value;
+  long int as_long_ints[2];
+  as_long_ints[0] = u.as_uint32s[0];
+  as_long_ints[1] = u.as_uint32s[1];
+  real_from_target (&real_value, as_long_ints, DFmode);
+  tree inner = build_real (inner_type, real_value);
+  return new rvalue (this, inner);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree).  */
+
+playback::rvalue *
+playback::context::
+new_rvalue_from_ptr (type *type,
+		     void *value)
+{
+  tree inner_type = type->as_tree ();
+  /* FIXME: how to ensure we have a wide enough type?  */
+  tree inner = build_int_cstu (inner_type, (unsigned HOST_WIDE_INT)value);
+  return new rvalue (this, inner);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree).  */
+
+playback::rvalue *
+playback::context::
+new_string_literal (const char *value)
+{
+  tree t_str = build_string (strlen (value), value);
+  gcc_assert (m_char_array_type_node);
+  TREE_TYPE (t_str) = m_char_array_type_node;
+
+  /* Convert to (const char*), loosely based on
+     c/c-typeck.c: array_to_pointer_conversion,
+     by taking address of start of string.  */
+  tree t_addr = build1 (ADDR_EXPR, m_const_char_ptr, t_str);
+
+  return new rvalue (this, t_addr);
+}
+
+/* Coerce a tree expression into a boolean tree expression.  */
+
+tree
+playback::context::
+as_truth_value (tree expr, location *loc)
+{
+  /* Compare to c-typeck.c:c_objc_common_truthvalue_conversion */
+  tree typed_zero = fold_build1 (CONVERT_EXPR,
+				 TREE_TYPE (expr),
+				 integer_zero_node);
+  if (loc)
+    set_tree_location (typed_zero, loc);
+
+  expr = build2 (NE_EXPR, integer_type_node, expr, typed_zero);
+  if (loc)
+    set_tree_location (expr, loc);
+
+  return expr;
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   unary op.  */
+
+playback::rvalue *
+playback::context::
+new_unary_op (location *loc,
+	      enum gcc_jit_unary_op op,
+	      type *result_type,
+	      rvalue *a)
+{
+  // FIXME: type-checking, or coercion?
+  enum tree_code inner_op;
+
+  gcc_assert (result_type);
+  gcc_assert (a);
+
+  tree node = a->as_tree ();
+  tree inner_result = NULL;
+
+  switch (op)
+    {
+    default:
+      add_error (loc, "unrecognized (enum gcc_jit_unary_op) value: %i", op);
+      return NULL;
+
+    case GCC_JIT_UNARY_OP_MINUS:
+      inner_op = NEGATE_EXPR;
+      break;
+
+    case GCC_JIT_UNARY_OP_BITWISE_NEGATE:
+      inner_op = BIT_NOT_EXPR;
+      break;
+
+    case GCC_JIT_UNARY_OP_LOGICAL_NEGATE:
+      node = as_truth_value (node, loc);
+      inner_result = invert_truthvalue (node);
+      if (loc)
+	set_tree_location (inner_result, loc);
+      return new rvalue (this, inner_result);
+    }
+
+  inner_result = build1 (inner_op,
+			 result_type->as_tree (),
+			 node);
+  if (loc)
+    set_tree_location (inner_result, loc);
+
+  return new rvalue (this, inner_result);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   binary op.  */
+
+playback::rvalue *
+playback::context::
+new_binary_op (location *loc,
+	       enum gcc_jit_binary_op op,
+	       type *result_type,
+	       rvalue *a, rvalue *b)
+{
+  // FIXME: type-checking, or coercion?
+  enum tree_code inner_op;
+
+  gcc_assert (result_type);
+  gcc_assert (a);
+  gcc_assert (b);
+
+  tree node_a = a->as_tree ();
+  tree node_b = b->as_tree ();
+
+  switch (op)
+    {
+    default:
+      add_error (loc, "unrecognized (enum gcc_jit_binary_op) value: %i", op);
+      return NULL;
+
+    case GCC_JIT_BINARY_OP_PLUS:
+      inner_op = PLUS_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_MINUS:
+      inner_op = MINUS_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_MULT:
+      inner_op = MULT_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_DIVIDE:
+      if (FLOAT_TYPE_P (result_type->as_tree ()))
+	/* Floating-point division: */
+	inner_op = RDIV_EXPR;
+      else
+	/* Truncating to zero: */
+	inner_op = TRUNC_DIV_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_MODULO:
+      inner_op = TRUNC_MOD_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_BITWISE_AND:
+      inner_op = BIT_AND_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_BITWISE_XOR:
+      inner_op = BIT_XOR_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_BITWISE_OR:
+      inner_op = BIT_IOR_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_LOGICAL_AND:
+      node_a = as_truth_value (node_a, loc);
+      node_b = as_truth_value (node_b, loc);
+      inner_op = TRUTH_ANDIF_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_LOGICAL_OR:
+      node_a = as_truth_value (node_a, loc);
+      node_b = as_truth_value (node_b, loc);
+      inner_op = TRUTH_ORIF_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_LSHIFT:
+      inner_op = LSHIFT_EXPR;
+      break;
+
+    case GCC_JIT_BINARY_OP_RSHIFT:
+      inner_op = RSHIFT_EXPR;
+      break;
+    }
+
+  tree inner_expr = build2 (inner_op,
+			    result_type->as_tree (),
+			    node_a,
+			    node_b);
+  if (loc)
+    set_tree_location (inner_expr, loc);
+
+  return new rvalue (this, inner_expr);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   comparison.  */
+
+playback::rvalue *
+playback::context::
+new_comparison (location *loc,
+		enum gcc_jit_comparison op,
+		rvalue *a, rvalue *b)
+{
+  // FIXME: type-checking, or coercion?
+  enum tree_code inner_op;
+
+  gcc_assert (a);
+  gcc_assert (b);
+
+  switch (op)
+    {
+    default:
+      add_error (loc, "unrecognized (enum gcc_jit_comparison) value: %i", op);
+      return NULL;
+
+    case GCC_JIT_COMPARISON_EQ:
+      inner_op = EQ_EXPR;
+      break;
+    case GCC_JIT_COMPARISON_NE:
+      inner_op = NE_EXPR;
+      break;
+    case GCC_JIT_COMPARISON_LT:
+      inner_op = LT_EXPR;
+      break;
+    case GCC_JIT_COMPARISON_LE:
+      inner_op = LE_EXPR;
+      break;
+    case GCC_JIT_COMPARISON_GT:
+      inner_op = GT_EXPR;
+      break;
+    case GCC_JIT_COMPARISON_GE:
+      inner_op = GE_EXPR;
+      break;
+    }
+
+  tree inner_expr = build2 (inner_op,
+			    boolean_type_node,
+			    a->as_tree (),
+			    b->as_tree ());
+  if (loc)
+    set_tree_location (inner_expr, loc);
+  return new rvalue (this, inner_expr);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   function call.  */
+
+playback::rvalue *
+playback::context::
+build_call (location *loc,
+	    tree fn_ptr,
+	    vec<rvalue *> args)
+{
+  vec<tree, va_gc> *tree_args;
+  vec_alloc (tree_args, args.length ());
+  for (unsigned i = 0; i < args.length (); i++)
+    tree_args->quick_push (args[i]->as_tree ());
+
+  if (loc)
+    set_tree_location (fn_ptr, loc);
+
+  tree fn = TREE_TYPE (fn_ptr);
+  tree fn_type = TREE_TYPE (fn);
+  tree return_type = TREE_TYPE (fn_type);
+
+  return new rvalue (this,
+		     build_call_vec (return_type,
+				     fn_ptr, tree_args));
+
+  /* see c-typeck.c: build_function_call
+     which calls build_function_call_vec
+
+     which does lots of checking, then:
+    result = build_call_array_loc (loc, TREE_TYPE (fntype),
+				   function, nargs, argarray);
+    which is in tree.c
+    (see also build_call_vec)
+   */
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   call to a specific function.  */
+
+playback::rvalue *
+playback::context::
+new_call (location *loc,
+	  function *func,
+	  vec<rvalue *> args)
+{
+  tree fndecl;
+
+  gcc_assert (func);
+
+  fndecl = func->as_fndecl ();
+
+  tree fntype = TREE_TYPE (fndecl);
+
+  tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
+
+  return build_call (loc, fn, args);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   call through a function pointer.  */
+
+playback::rvalue *
+playback::context::
+new_call_through_ptr (location *loc,
+		      rvalue *fn_ptr,
+		      vec<rvalue *> args)
+{
+  gcc_assert (fn_ptr);
+  tree t_fn_ptr = fn_ptr->as_tree ();
+
+  return build_call (loc, t_fn_ptr, args);
+}
+
+/* Construct a tree for a cast.  */
+
+tree
+playback::context::build_cast (playback::location *loc,
+			       playback::rvalue *expr,
+			       playback::type *type_)
+{
+  /* For comparison, see:
+     - c/c-typeck.c:build_c_cast
+     - c/c-convert.c: convert
+     - convert.h
+
+     Only some kinds of cast are currently supported here.  */
+  tree t_expr = expr->as_tree ();
+  tree t_dst_type = type_->as_tree ();
+  tree t_ret = NULL;
+  t_ret = targetm.convert_to_type (t_dst_type, t_expr);
+  if (t_ret)
+      return t_ret;
+  enum tree_code dst_code = TREE_CODE (t_dst_type);
+  switch (dst_code)
+    {
+    case INTEGER_TYPE:
+    case ENUMERAL_TYPE:
+      t_ret = convert_to_integer (t_dst_type, t_expr);
+      goto maybe_fold;
+
+    case BOOLEAN_TYPE:
+      /* Compare with c_objc_common_truthvalue_conversion and
+	 c_common_truthvalue_conversion. */
+      /* For now, convert to: (t_expr != 0)  */
+      t_ret = build2 (NE_EXPR, t_dst_type,
+		      t_expr, integer_zero_node);
+      goto maybe_fold;
+
+    case REAL_TYPE:
+      t_ret = convert_to_real (t_dst_type, t_expr);
+      goto maybe_fold;
+
+    case POINTER_TYPE:
+      t_ret = build1 (NOP_EXPR, t_dst_type, t_expr);
+      goto maybe_fold;
+
+    default:
+      add_error (loc, "couldn't handle cast during playback");
+      fprintf (stderr, "input expression:\n");
+      debug_tree (t_expr);
+      fprintf (stderr, "requested type:\n");
+      debug_tree (t_dst_type);
+      return error_mark_node;
+
+    maybe_fold:
+      if (TREE_CODE (t_ret) != C_MAYBE_CONST_EXPR)
+	t_ret = fold (t_ret);
+      return t_ret;
+    }
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   cast.  */
+
+playback::rvalue *
+playback::context::
+new_cast (playback::location *loc,
+	  playback::rvalue *expr,
+	  playback::type *type_)
+{
+
+  tree t_cast = build_cast (loc, expr, type_);
+  if (loc)
+    set_tree_location (t_cast, loc);
+  return new rvalue (this, t_cast);
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree) for an
+   array access.  */
+
+playback::lvalue *
+playback::context::
+new_array_access (location *loc,
+		  rvalue *ptr,
+		  rvalue *index)
+{
+  gcc_assert (ptr);
+  gcc_assert (index);
+
+  /* For comparison, see:
+       c/c-typeck.c: build_array_ref
+       c-family/c-common.c: pointer_int_sum
+  */
+  tree t_ptr = ptr->as_tree ();
+  tree t_index = index->as_tree ();
+  tree t_type_ptr = TREE_TYPE (t_ptr);
+  tree t_type_star_ptr = TREE_TYPE (t_type_ptr);
+
+  if (TREE_CODE (t_type_ptr) == ARRAY_TYPE)
+    {
+      tree t_result = build4 (ARRAY_REF, t_type_star_ptr, t_ptr, t_index,
+			      NULL_TREE, NULL_TREE);
+      if (loc)
+        set_tree_location (t_result, loc);
+      return new lvalue (this, t_result);
+    }
+  else
+    {
+      /* Convert index to an offset in bytes.  */
+      tree t_sizeof = size_in_bytes (t_type_star_ptr);
+      t_index = fold_build1 (CONVERT_EXPR, sizetype, t_index);
+      tree t_offset = build2 (MULT_EXPR, sizetype, t_index, t_sizeof);
+
+      /* Locate (ptr + offset).  */
+      tree t_address = build2 (POINTER_PLUS_EXPR, t_type_ptr, t_ptr, t_offset);
+
+      tree t_indirection = build1 (INDIRECT_REF, t_type_star_ptr, t_address);
+      if (loc)
+        {
+          set_tree_location (t_sizeof, loc);
+          set_tree_location (t_offset, loc);
+          set_tree_location (t_address, loc);
+          set_tree_location (t_indirection, loc);
+        }
+
+      return new lvalue (this, t_indirection);
+    }
+}
+
+/* Construct a tree for a field access.  */
+
+tree
+playback::context::
+new_field_access (location *loc,
+		  tree datum,
+		  field *field)
+{
+  gcc_assert (datum);
+  gcc_assert (field);
+
+  /* Compare with c/c-typeck.c:lookup_field, build_indirect_ref, and
+     build_component_ref. */
+  tree type = TREE_TYPE (datum);
+  gcc_assert (type);
+  gcc_assert (TREE_CODE (type) != POINTER_TYPE);
+
+ tree t_field = field->as_tree ();
+ tree ref = build3 (COMPONENT_REF, TREE_TYPE (t_field), datum,
+		     t_field, NULL_TREE);
+  if (loc)
+    set_tree_location (ref, loc);
+  return ref;
+}
+
+/* Construct a tree for a dereference.  */
+
+tree
+playback::context::
+new_dereference (tree ptr,
+		 location *loc)
+{
+  gcc_assert (ptr);
+
+  tree type = TREE_TYPE (TREE_TYPE(ptr));
+  tree datum = build1 (INDIRECT_REF, type, ptr);
+  if (loc)
+    set_tree_location (datum, loc);
+  return datum;
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree) for a
+   field access.  */
+
+playback::lvalue *
+playback::lvalue::
+access_field (location *loc,
+	      field *field)
+{
+  tree datum = as_tree ();
+  tree ref = get_context ()->new_field_access (loc, datum, field);
+  if (!ref)
+    return NULL;
+  return new lvalue (get_context (), ref);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   field access.  */
+
+playback::rvalue *
+playback::rvalue::
+access_field (location *loc,
+	      field *field)
+{
+  tree datum = as_tree ();
+  tree ref = get_context ()->new_field_access (loc, datum, field);
+  if (!ref)
+    return NULL;
+  return new rvalue (get_context (), ref);
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree) for a
+   dereferenced field access.  */
+
+playback::lvalue *
+playback::rvalue::
+dereference_field (location *loc,
+		   field *field)
+{
+  tree ptr = as_tree ();
+  tree datum = get_context ()->new_dereference (ptr, loc);
+  if (!datum)
+    return NULL;
+  tree ref = get_context ()->new_field_access (loc, datum, field);
+  if (!ref)
+    return NULL;
+  return new lvalue (get_context (), ref);
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree) for a
+   dereference.  */
+
+playback::lvalue *
+playback::rvalue::
+dereference (location *loc)
+{
+  tree ptr = as_tree ();
+  tree datum = get_context ()->new_dereference (ptr, loc);
+  return new lvalue (get_context (), datum);
+}
+
+/* Construct a playback::rvalue instance (wrapping a tree) for an
+   address-lookup.  */
+
+playback::rvalue *
+playback::lvalue::
+get_address (location *loc)
+{
+  tree t_lvalue = as_tree ();
+  tree t_thistype = TREE_TYPE (t_lvalue);
+  tree t_ptrtype = build_pointer_type (t_thistype);
+  tree ptr = build1 (ADDR_EXPR, t_ptrtype, t_lvalue);
+  if (loc)
+    get_context ()->set_tree_location (ptr, loc);
+  return new rvalue (get_context (), ptr);
+}
+
+/* gcc::jit::playback::wrapper subclasses are GC-managed:
+   allocate them using ggc_internal_cleared_alloc.  */
+
+void *
+playback::wrapper::
+operator new (size_t sz)
+{
+  return ggc_internal_cleared_alloc (sz MEM_STAT_INFO);
+}
+
+/* Constructor for gcc:jit::playback::function.  */
+
+playback::function::
+function (context *ctxt,
+	  tree fndecl,
+	  enum gcc_jit_function_kind kind)
+: m_ctxt(ctxt),
+  m_inner_fndecl (fndecl),
+  m_inner_bind_expr (NULL),
+  m_kind (kind)
+{
+  if (m_kind != GCC_JIT_FUNCTION_IMPORTED)
+    {
+      /* Create a BIND_EXPR, and within it, a statement list.  */
+      m_stmt_list = alloc_stmt_list ();
+      m_stmt_iter = tsi_start (m_stmt_list);
+      m_inner_block = make_node (BLOCK);
+      m_inner_bind_expr =
+	build3 (BIND_EXPR, void_type_node, NULL, m_stmt_list, m_inner_block);
+    }
+  else
+    {
+      m_inner_block = NULL;
+      m_stmt_list = NULL;
+    }
+}
+
+/* Hand-written GC-marking hook for playback functions.  */
+
+void
+playback::function::
+gt_ggc_mx ()
+{
+  gt_ggc_m_9tree_node (m_inner_fndecl);
+  gt_ggc_m_9tree_node (m_inner_bind_expr);
+  gt_ggc_m_9tree_node (m_stmt_list);
+  gt_ggc_m_9tree_node (m_inner_block);
+}
+
+/* Get the return type of a playback function, in tree form.  */
+
+tree
+playback::function::
+get_return_type_as_tree () const
+{
+  return TREE_TYPE (TREE_TYPE(m_inner_fndecl));
+}
+
+/* Construct a new local within this playback::function.  */
+
+playback::lvalue *
+playback::function::
+new_local (location *loc,
+	   type *type,
+	   const char *name)
+{
+  gcc_assert (type);
+  gcc_assert (name);
+  tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			   get_identifier (name),
+			   type->as_tree ());
+  DECL_CONTEXT (inner) = this->m_inner_fndecl;
+
+  /* Prepend to BIND_EXPR_VARS: */
+  DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
+  BIND_EXPR_VARS (m_inner_bind_expr) = inner;
+
+  if (loc)
+    set_tree_location (inner, loc);
+  return new lvalue (m_ctxt, inner);
+}
+
+/* Construct a new block within this playback::function.  */
+
+playback::block *
+playback::function::
+new_block (const char *name)
+{
+  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
+
+  block *result = new playback::block (this, name);
+  m_blocks.safe_push (result);
+  return result;
+}
+
+/* Build a statement list for the function as a whole out of the
+   lists of statements for the individual blocks, building labels
+   for each block.  */
+
+void
+playback::function::
+build_stmt_list ()
+{
+  int i;
+  block *b;
+
+  FOR_EACH_VEC_ELT (m_blocks, i, b)
+    {
+      int j;
+      tree stmt;
+
+      b->m_label_expr = build1 (LABEL_EXPR,
+				void_type_node,
+				b->as_label_decl ());
+      tsi_link_after (&m_stmt_iter, b->m_label_expr, TSI_CONTINUE_LINKING);
+
+      FOR_EACH_VEC_ELT (b->m_stmts, j, stmt)
+	tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
+    }
+}
+
+/* Finish compiling the given function, potentially running the
+   garbage-collector.
+   The function will have a statement list by now.
+   Amongst other things, this gimplifies the statement list,
+   and calls cgraph_node::finalize_function on the function.  */
+
+void
+playback::function::
+postprocess ()
+{
+  if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE))
+    debug_tree (m_stmt_list);
+
+  /* Do we need this to force cgraphunit.c to output the function? */
+  if (m_kind == GCC_JIT_FUNCTION_EXPORTED)
+    {
+      DECL_EXTERNAL (m_inner_fndecl) = 0;
+      DECL_PRESERVE_P (m_inner_fndecl) = 1;
+    }
+
+  if (m_kind == GCC_JIT_FUNCTION_INTERNAL
+      ||m_kind == GCC_JIT_FUNCTION_ALWAYS_INLINE)
+    {
+      DECL_EXTERNAL (m_inner_fndecl) = 0;
+      TREE_PUBLIC (m_inner_fndecl) = 0;
+    }
+
+  if (m_kind != GCC_JIT_FUNCTION_IMPORTED)
+    {
+      /* Seem to need this in gimple-low.c: */
+      gcc_assert (m_inner_block);
+      DECL_INITIAL (m_inner_fndecl) = m_inner_block;
+
+      /* how to add to function? the following appears to be how to
+	 set the body of a m_inner_fndecl: */
+      DECL_SAVED_TREE(m_inner_fndecl) = m_inner_bind_expr;
+
+      /* Ensure that locals appear in the debuginfo.  */
+      BLOCK_VARS (m_inner_block) = BIND_EXPR_VARS (m_inner_bind_expr);
+
+      //debug_tree (m_inner_fndecl);
+
+      /* Convert to gimple: */
+      //printf("about to gimplify_function_tree\n");
+      gimplify_function_tree (m_inner_fndecl);
+      //printf("finished gimplify_function_tree\n");
+
+      current_function_decl = m_inner_fndecl;
+      if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE))
+	dump_function_to_file (m_inner_fndecl, stderr, TDF_VOPS|TDF_MEMSYMS|TDF_LINENO);
+      //debug_tree (m_inner_fndecl);
+
+      //printf("about to add to cgraph\n");
+      /* Add to cgraph: */
+      cgraph_node::finalize_function (m_inner_fndecl, false);
+      /* This can trigger a collection, so we need to have all of
+	 the funcs as roots.  */
+
+      current_function_decl = NULL;
+    }
+}
+
+/* Add an eval of the rvalue to the function's statement list.  */
+
+void
+playback::block::
+add_eval (location *loc,
+	  rvalue *rvalue)
+{
+  gcc_assert (rvalue);
+
+  if (loc)
+    set_tree_location (rvalue->as_tree (), loc);
+
+  add_stmt (rvalue->as_tree ());
+}
+
+/* Add an assignment to the function's statement list.  */
+
+void
+playback::block::
+add_assignment (location *loc,
+		lvalue *lvalue,
+		rvalue *rvalue)
+{
+  gcc_assert (lvalue);
+  gcc_assert (rvalue);
+
+  tree t_lvalue = lvalue->as_tree ();
+  tree t_rvalue = rvalue->as_tree ();
+  if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
+    {
+      t_rvalue = build1 (CONVERT_EXPR,
+		         TREE_TYPE (t_lvalue),
+		         t_rvalue);
+      if (loc)
+	set_tree_location (t_rvalue, loc);
+    }
+
+  tree stmt =
+    build2 (MODIFY_EXPR, TREE_TYPE (t_lvalue),
+	    t_lvalue, t_rvalue);
+  if (loc)
+    set_tree_location (stmt, loc);
+  add_stmt (stmt);
+}
+
+/* Add a comment to the function's statement list.
+   For now this is done by adding a dummy label.  */
+
+void
+playback::block::
+add_comment (location *loc,
+	     const char *text)
+{
+  /* Wrap the text in C-style comment delimiters.  */
+  size_t sz =
+    (3 /* opening delim */
+     + strlen (text)
+     + 3 /* closing delim */
+     + 1 /* terminator */);
+  char *wrapped = (char *)ggc_internal_alloc (sz);
+  snprintf (wrapped, sz, "/* %s */", text);
+
+  /* For now we simply implement this by adding a dummy label with a name
+     containing the given text.  */
+  tree identifier = get_identifier (wrapped);
+  tree label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
+				identifier, void_type_node);
+  DECL_CONTEXT (label_decl) = m_func->as_fndecl ();
+
+  tree label_expr = build1 (LABEL_EXPR, void_type_node, label_decl);
+  if (loc)
+    set_tree_location (label_expr, loc);
+  add_stmt (label_expr);
+}
+
+/* Add a conditional jump statement to the function's statement list.  */
+
+void
+playback::block::
+add_conditional (location *loc,
+		 rvalue *boolval,
+		 block *on_true,
+		 block *on_false)
+{
+  gcc_assert (boolval);
+  gcc_assert (on_true);
+  gcc_assert (on_false);
+
+  /* COND_EXPR wants statement lists for the true/false operands, but we
+     want labels.
+     Shim it by creating jumps to the labels */
+  tree true_jump = build1 (GOTO_EXPR, void_type_node,
+			   on_true->as_label_decl ());
+  if (loc)
+    set_tree_location (true_jump, loc);
+
+  tree false_jump = build1 (GOTO_EXPR, void_type_node,
+			    on_false->as_label_decl ());
+  if (loc)
+    set_tree_location (false_jump, loc);
+
+  tree stmt =
+    build3 (COND_EXPR, void_type_node, boolval->as_tree (),
+	    true_jump, false_jump);
+  if (loc)
+    set_tree_location (stmt, loc);
+  add_stmt (stmt);
+}
+
+/* Add an unconditional jump statement to the function's statement list.  */
+
+void
+playback::block::
+add_jump (location *loc,
+	  block *target)
+{
+  gcc_assert (target);
+
+  // see c_finish_loop
+  //tree top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+  //add_stmt (top);
+
+  //tree stmt = build_and_jump (&LABEL_EXPR_LABEL (target->label_));
+  TREE_USED (target->as_label_decl ()) = 1;
+  tree stmt = build1 (GOTO_EXPR, void_type_node, target->as_label_decl ());
+  if (loc)
+    set_tree_location (stmt, loc);
+  add_stmt (stmt);
+
+  /*
+  from c-typeck.c:
+tree
+c_finish_goto_label (location_t loc, tree label)
+{
+  tree decl = lookup_label_for_goto (loc, label);
+  if (!decl)
+    return NULL_TREE;
+  TREE_USED (decl) = 1;
+  {
+    tree t = build1 (GOTO_EXPR, void_type_node, decl);
+    SET_EXPR_LOCATION (t, loc);
+    return add_stmt (t);
+  }
+}
+  */
+
+}
+
+/* Add a return statement to the function's statement list.  */
+
+void
+playback::block::
+add_return (location *loc,
+	    rvalue *rvalue)
+{
+  tree modify_retval = NULL;
+  tree return_type = m_func->get_return_type_as_tree ();
+  if (rvalue)
+    {
+      tree t_lvalue = DECL_RESULT (m_func->as_fndecl ());
+      tree t_rvalue = rvalue->as_tree ();
+      if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
+	t_rvalue = build1 (CONVERT_EXPR,
+			   TREE_TYPE (t_lvalue),
+			   t_rvalue);
+      modify_retval = build2 (MODIFY_EXPR, return_type,
+			      t_lvalue, t_rvalue);
+      if (loc)
+	set_tree_location (modify_retval, loc);
+    }
+  tree return_stmt = build1 (RETURN_EXPR, return_type,
+			     modify_retval);
+  if (loc)
+    set_tree_location (return_stmt, loc);
+
+  add_stmt (return_stmt);
+}
+
+/* Constructor for gcc::jit::playback::block.  */
+
+playback::block::
+block (function *func,
+       const char *name)
+: m_func (func),
+  m_stmts ()
+{
+  tree identifier;
+
+  gcc_assert (func);
+  // name can be NULL
+  if (name)
+    identifier = get_identifier (name);
+  else
+    identifier = NULL;
+  m_label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
+			    identifier, void_type_node);
+  DECL_CONTEXT (m_label_decl) = func->as_fndecl ();
+  m_label_expr = NULL;
+}
+
+/* Construct a tempdir path template suitable for use by mkdtemp
+   e.g. "/tmp/libgccjit-XXXXXX", but respecting the rules in
+   libiberty's choose_tempdir rather than hardcoding "/tmp/".
+
+   The memory is allocated using malloc and must be freed.
+   Aborts the process if allocation fails. */
+
+static char *
+make_tempdir_path_template ()
+{
+  const char *tmpdir_buf;
+  size_t tmpdir_len;
+  const char *file_template_buf;
+  size_t file_template_len;
+  char *result;
+
+  /* The result of choose_tmpdir is a cached buffer within libiberty, so
+     we must *not* free it.  */
+  tmpdir_buf = choose_tmpdir ();
+
+  /* choose_tmpdir aborts on malloc failure.  */
+  gcc_assert (tmpdir_buf);
+
+  tmpdir_len = strlen (tmpdir_buf);
+  /* tmpdir_buf should now have a dir separator as the final byte.  */
+  gcc_assert (tmpdir_len > 0);
+  gcc_assert (tmpdir_buf[tmpdir_len - 1] == DIR_SEPARATOR);
+
+  file_template_buf = "libgccjit-XXXXXX";
+  file_template_len = strlen (file_template_buf);
+
+  result = XNEWVEC (char, tmpdir_len + file_template_len + 1);
+  strcpy (result, tmpdir_buf);
+  strcpy (result + tmpdir_len, file_template_buf);
+
+  return result;
+}
+
+/* Compile a playback::context:
+
+   - Use the context's options to cconstruct command-line options, and
+     call into the rest of GCC (toplev::main).
+   - Assuming it succeeds, we have a .s file; we want a .so file.
+     Invoke another gcc to convert the .s file to a .so file.
+   - dlopen the .so file
+   - Wrap the result up as a playback::result and return it.  */
+
+result *
+playback::context::
+compile ()
+{
+  void *handle = NULL;
+  const char *ctxt_progname;
+  result *result_obj = NULL;
+  const char *fake_args[20];
+  unsigned int num_args;
+
+  m_path_template = make_tempdir_path_template ();
+  if (!m_path_template)
+    return NULL;
+
+  /* Create tempdir using mkdtemp.  This is created with 0700 perms and
+     is unique.  Hence no other (non-root) users should have access to
+     the paths within it.  */
+  m_path_tempdir = mkdtemp (m_path_template);
+  if (!m_path_tempdir)
+    return NULL;
+  m_path_c_file = concat (m_path_tempdir, "/fake.c", NULL);
+  m_path_s_file = concat (m_path_tempdir, "/fake.s", NULL);
+  m_path_so_file = concat (m_path_tempdir, "/fake.so", NULL);
+
+  /* Call into the rest of gcc.
+     For now, we have to assemble command-line options to pass into
+     toplev::main, so that they can be parsed. */
+
+  /* Pass in user-provided program name as argv0, if any, so that it
+     makes it into GCC's "progname" global, used in various diagnostics. */
+  ctxt_progname = get_str_option (GCC_JIT_STR_OPTION_PROGNAME);
+  fake_args[0] =
+    (ctxt_progname ? ctxt_progname : "libgccjit.so");
+
+  fake_args[1] = m_path_c_file;
+  num_args = 2;
+
+#define ADD_ARG(arg) \
+  do \
+    { \
+      gcc_assert(num_args < sizeof(fake_args)/sizeof(char*)); \
+      fake_args[num_args++] = arg; \
+    } \
+  while (0)
+
+  ADD_ARG ("-fPIC");
+
+  /* Handle int options: */
+  switch (get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL))
+    {
+    default:
+      add_error (NULL,
+		 "unrecognized optimization level: %i",
+		 get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL));
+      return NULL;
+
+    case 0:
+      ADD_ARG ("-O0");
+      break;
+
+    case 1:
+      ADD_ARG ("-O1");
+      break;
+
+    case 2:
+      ADD_ARG ("-O2");
+      break;
+
+    case 3:
+      ADD_ARG ("-O3");
+      break;
+    }
+  /* What about -Os? */
+
+  /* Handle bool options: */
+  if (get_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO))
+    ADD_ARG ("-g");
+
+  /* Suppress timing (and other) info.  */
+  if (!get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_SUMMARY))
+    {
+      ADD_ARG ("-quiet");
+      quiet_flag = 1;
+    }
+
+  /* Aggressively garbage-collect, to shake out bugs: */
+  if (get_bool_option (GCC_JIT_BOOL_OPTION_SELFCHECK_GC))
+    {
+      ADD_ARG ("--param");
+      ADD_ARG ("ggc-min-expand=0");
+      ADD_ARG ("--param");
+      ADD_ARG ("ggc-min-heapsize=0");
+    }
+
+  if (get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING))
+    {
+      ADD_ARG ("-fdump-tree-all");
+      ADD_ARG ("-fdump-rtl-all");
+      ADD_ARG ("-fdump-ipa-all");
+    }
+
+  toplev toplev (false);
+
+  toplev.main (num_args, const_cast <char **> (fake_args));
+  toplev.finalize ();
+
+  active_playback_ctxt = NULL;
+
+  if (errors_occurred ())
+    return NULL;
+
+  if (get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE))
+   dump_generated_code ();
+
+  /* Gross hacks follow:
+     We have a .s file; we want a .so file.
+     We could reuse parts of gcc/gcc.c to do this.
+     For now, just use the driver binary from the install, as
+     named in gcc-driver-name.h
+     e.g. "x86_64-unknown-linux-gnu-gcc-5.0.0".
+   */
+  {
+    auto_timevar assemble_timevar (TV_ASSEMBLE);
+    const char *errmsg;
+    const char *argv[7];
+    int exit_status = 0;
+    int err = 0;
+    const char *gcc_driver_name = GCC_DRIVER_NAME;
+
+    argv[0] = gcc_driver_name;
+    argv[1] = "-shared";
+    /* The input: assembler.  */
+    argv[2] = m_path_s_file;
+    /* The output: shared library.  */
+    argv[3] = "-o";
+    argv[4] = m_path_so_file;
+
+    /* Don't use the linker plugin.
+       If running with just a "make" and not a "make install", then we'd
+       run into
+          "fatal error: -fuse-linker-plugin, but liblto_plugin.so not found"
+       libto_plugin is a .la at build time, with it becoming installed with
+       ".so" suffix: i.e. it doesn't exist with a .so suffix until install
+       time.  */
+    argv[5] = "-fno-use-linker-plugin";
+
+    /* pex argv arrays are NULL-terminated.  */
+    argv[6] = NULL;
+
+    errmsg = pex_one (PEX_SEARCH, /* int flags, */
+		      gcc_driver_name,
+		      const_cast<char * const *> (argv),
+		      ctxt_progname, /* const char *pname */
+		      NULL, /* const char *outname */
+		      NULL, /* const char *errname */
+		      &exit_status, /* int *status */
+		      &err); /* int *err*/
+    if (errmsg)
+      {
+	add_error (NULL, "error invoking gcc driver: %s", errmsg);
+	return NULL;
+      }
+
+    /* pex_one can return a NULL errmsg when the executable wasn't
+       found (or doesn't exist), so trap these cases also.  */
+    if (exit_status || err)
+      {
+	add_error (NULL,
+		   "error invoking gcc driver: exit_status: %i err: %i",
+		   exit_status, err);
+	add_error (NULL,
+		   "whilst attempting to run a driver named: %s",
+		   gcc_driver_name);
+	add_error (NULL,
+		   "PATH was: %s",
+		   getenv ("PATH"));
+	return NULL;
+      }
+  }
+
+  // TODO: split out assembles vs linker
+
+  /* dlopen the .so file. */
+  {
+    auto_timevar load_timevar (TV_LOAD);
+
+    const char *error;
+
+    /* Clear any existing error.  */
+    dlerror ();
+
+    handle = dlopen (m_path_so_file, RTLD_NOW | RTLD_LOCAL);
+    if ((error = dlerror()) != NULL)  {
+      add_error (NULL, "%s", error);
+    }
+    if (handle)
+      result_obj = new result (handle);
+    else
+      result_obj = NULL;
+  }
+
+  return result_obj;
+}
+
+/* Top-level hook for playing back a recording context.
+
+   This plays back m_recording_ctxt, and, if no errors
+   occurred builds statement lists for and then postprocesses
+   every function in the result.  */
+
+void
+playback::context::
+replay ()
+{
+  /* Adapted from c-common.c:c_common_nodes_and_builtins.  */
+  tree array_domain_type = build_index_type (size_int (200));
+  m_char_array_type_node
+    = build_array_type (char_type_node, array_domain_type);
+
+  m_const_char_ptr
+    = build_pointer_type (build_qualified_type (char_type_node,
+						TYPE_QUAL_CONST));
+
+  /* Replay the recorded events:  */
+  timevar_push (TV_JIT_REPLAY);
+
+  m_recording_ctxt->replay_into (this);
+
+  /* Clean away the temporary references from recording objects
+     to playback objects.  We have to do this now since the
+     latter are GC-allocated, but the former don't mark these
+     refs.  Hence we must stop using them before the GC can run.  */
+  m_recording_ctxt->disassociate_from_playback ();
+
+  timevar_pop (TV_JIT_REPLAY);
+
+  if (!errors_occurred ())
+    {
+      int i;
+      function *func;
+
+      /* No GC can happen yet; process the cached source locations.  */
+      handle_locations ();
+
+      /* We've now created tree nodes for the stmts in the various blocks
+	 in each function, but we haven't built each function's single stmt
+	 list yet.  Do so now.  */
+      FOR_EACH_VEC_ELT (m_functions, i, func)
+	func->build_stmt_list ();
+
+      /* No GC can have happened yet.  */
+
+      /* Postprocess the functions.  This could trigger GC.  */
+      FOR_EACH_VEC_ELT (m_functions, i, func)
+	{
+	  gcc_assert (func);
+	  func->postprocess ();
+	}
+    }
+}
+
+/* Dump the generated .s file to stderr.  */
+
+void
+playback::context::
+dump_generated_code ()
+{
+  char buf[4096];
+  size_t sz;
+  FILE *f_in = fopen (m_path_s_file, "r");
+  if (!f_in)
+    return;
+
+  while ( (sz = fread (buf, 1, sizeof (buf), f_in)) )
+    fwrite (buf, 1, sz, stderr);
+
+  fclose (f_in);
+}
+
+/* qsort comparator for comparing pairs of playback::source_line *,
+   ordering them by line number.  */
+
+static int
+line_comparator (const void *lhs, const void *rhs)
+{
+  const playback::source_line *line_lhs = \
+    *static_cast<const playback::source_line * const*> (lhs);
+  const playback::source_line *line_rhs = \
+    *static_cast<const playback::source_line * const*> (rhs);
+  return line_lhs->get_line_num () - line_rhs->get_line_num ();
+}
+
+/* qsort comparator for comparing pairs of playback::location *,
+   ordering them by column number.  */
+
+static int
+location_comparator (const void *lhs, const void *rhs)
+{
+  const playback::location *loc_lhs = \
+    *static_cast<const playback::location * const *> (lhs);
+  const playback::location *loc_rhs = \
+    *static_cast<const playback::location * const *> (rhs);
+  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
+}
+
+/* Our API allows locations to be created in arbitrary orders, but the
+   linemap API requires locations to be created in ascending order
+   as if we were tokenizing files.
+
+   This hook sorts all of the the locations that have been created, and
+   calls into the linemap API, creating linemap entries in sorted order
+   for our locations.  */
+
+void
+playback::context::
+handle_locations ()
+{
+  /* Create the source code locations, following the ordering rules
+     imposed by the linemap API.
+
+     line_table is a global.  */
+  int i;
+  source_file *file;
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    {
+      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
+
+      /* Sort lines by ascending line numbers.  */
+      file->m_source_lines.qsort (&line_comparator);
+
+      int j;
+      source_line *line;
+      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
+	{
+	  int k;
+	  location *loc;
+
+	  /* Sort locations in line by ascending column numbers.  */
+	  line->m_locations.qsort (&location_comparator);
+
+	  /* Determine maximum column within this line.  */
+	  gcc_assert (line->m_locations.length () > 0);
+	  location *final_column =
+	    line->m_locations[line->m_locations.length () - 1];
+	  int max_col = final_column->get_column_num ();
+
+	  linemap_line_start (line_table, line->get_line_num (), max_col);
+	  FOR_EACH_VEC_ELT (line->m_locations, k, loc)
+	    {
+	      loc->m_srcloc =					   \
+		linemap_position_for_column (line_table, loc->get_column_num ());
+	    }
+	}
+
+      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+    }
+
+  /* line_table should now be populated; every playback::location should
+     now have an m_srcloc.  */
+
+  if (0)
+    line_table_dump (stderr,
+		     line_table,
+		     LINEMAPS_ORDINARY_USED (line_table),
+		     LINEMAPS_MACRO_USED (line_table));
+
+  /* Now assign them to tree nodes as appropriate.  */
+  std::pair<tree, location *> *cached_location;
+
+  FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location)
+    {
+      tree t = cached_location->first;
+      source_location srcloc = cached_location->second->m_srcloc;
+#if 0
+      inform (srcloc, "location of ");
+      debug_tree (t);
+#endif
+
+      /* This covers expressions: */
+      if (CAN_HAVE_LOCATION_P (t))
+	SET_EXPR_LOCATION (t, srcloc);
+      else if (CODE_CONTAINS_STRUCT(TREE_CODE(t), TS_DECL_MINIMAL))
+	DECL_SOURCE_LOCATION (t) = srcloc;
+      else
+	{
+	  /* Don't know how to set location on this node.  */
+	  if (0)
+	    {
+	      fprintf (stderr, "can't set location on:");
+	      debug_tree (t);
+	      fprintf (stderr, "\n");
+	    }
+	}
+    }
+}
+
+/* We handle errors on a playback::context by adding them to the
+   corresponding recording::context.  */
+
+void
+playback::context::
+add_error (location *loc, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
+				  fmt, ap);
+  va_end (ap);
+}
+
+/* We handle errors on a playback::context by adding them to the
+   corresponding recording::context.  */
+
+void
+playback::context::
+add_error_va (location *loc, const char *fmt, va_list ap)
+{
+  m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
+				  fmt, ap);
+}
+
+/* Constructor for gcc::jit::playback::result.  */
+
+result::
+result(void *dso_handle)
+  : m_dso_handle(dso_handle)
+{
+}
+
+/* gcc::jit::playback::result's destructor.
+
+   Called implicitly by gcc_jit_result_release.  */
+
+result::~result()
+{
+  dlclose (m_dso_handle);
+}
+
+/* Attempt to locate the given function by name within the
+   playback::result, using dlsym.
+
+   Implements the post-error-checking part of
+   gcc_jit_result_get_code.  */
+
+void *
+result::
+get_code (const char *funcname)
+{
+  void *code;
+  const char *error;
+
+  /* Clear any existing error.  */
+  dlerror ();
+
+  code = dlsym (m_dso_handle, funcname);
+
+  if ((error = dlerror()) != NULL)  {
+    fprintf(stderr, "%s\n", error);
+  }
+
+  return code;
+}
+
+/* Dealing with the linemap API.  */
+
+/* Construct a playback::location for a recording::location, if it
+   doesn't exist already.  */
+
+playback::location *
+playback::context::
+new_location (recording::location *rloc,
+	      const char *filename,
+	      int line,
+	      int column)
+{
+  /* Get the source_file for filename, creating if necessary.  */
+  source_file *src_file = get_source_file (filename);
+  /* Likewise for the line within the file.  */
+  source_line *src_line = src_file->get_source_line (line);
+  /* Likewise for the column within the line.  */
+  location *loc = src_line->get_location (rloc, column);
+  return loc;
+}
+
+/* Deferred setting of the location for a given tree, by adding the
+   (tree, playback::location) pair to a list of deferred associations.
+   We will actually set the location on the tree later on once
+   the source_location for the playback::location exists.  */
+
+void
+playback::context::
+set_tree_location (tree t, location *loc)
+{
+  gcc_assert (loc);
+  m_cached_locations.safe_push (std::make_pair (t, loc));
+}
+
+
+/* Construct a playback::source_file for the given source
+   filename, if it doesn't exist already.  */
+
+playback::source_file *
+playback::context::
+get_source_file (const char *filename)
+{
+  /* Locate the file.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_file *file;
+  tree ident_filename = get_identifier (filename);
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    if (file->filename_as_tree () == ident_filename)
+      return file;
+
+  /* Not found.  */
+  file = new source_file (ident_filename);
+  m_source_files.safe_push (file);
+  return file;
+}
+
+/* Constructor for gcc::jit::playback::source_file.  */
+
+playback::source_file::source_file (tree filename) :
+  m_source_lines (),
+  m_filename (filename)
+{
+}
+
+/* Construct a playback::source_line for the given line
+   within this source file, if one doesn't exist already.  */
+
+playback::source_line *
+playback::source_file::
+get_source_line (int line_num)
+{
+  /* Locate the line.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_line *line;
+
+  FOR_EACH_VEC_ELT (m_source_lines, i, line)
+    if (line->get_line_num () == line_num)
+      return line;
+
+  /* Not found.  */
+  line = new source_line (this, line_num);
+  m_source_lines.safe_push (line);
+  return line;
+}
+
+/* Constructor for gcc::jit::playback::source_line.  */
+
+playback::source_line::source_line (source_file *file, int line_num) :
+  m_locations (),
+  m_source_file (file),
+  m_line_num (line_num)
+{
+}
+
+/* Construct a playback::location for the given column
+   within this line of a specific source file, if one doesn't exist
+   already.  */
+
+playback::location *
+playback::source_line::
+get_location (recording::location *rloc, int column_num)
+{
+  int i;
+  location *loc;
+
+  /* Another linear search that probably should be a hash table.  */
+  FOR_EACH_VEC_ELT (m_locations, i, loc)
+    if (loc->get_column_num () == column_num)
+      return loc;
+
+  /* Not found.  */
+  loc = new location (rloc, this, column_num);
+  m_locations.safe_push (loc);
+  return loc;
+}
+
+/* Constructor for gcc::jit::playback::location.  */
+
+playback::location::location (recording::location *loc,
+			      source_line *line,
+			      int column_num) :
+  m_srcloc (UNKNOWN_LOCATION),
+  m_recording_loc (loc),
+  m_line (line),
+  m_column_num(column_num)
+{
+}
+
+/* The active gcc::jit::playback::context instance.  This is a singleton,
+   guarded by jit_mutex.  */
+
+playback::context *active_playback_ctxt;
+
+} // namespace gcc::jit
+
+} // namespace gcc
-- 
1.8.5.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]