Committed to branch dmalcolm/jit:

This adds two public API entrypoints:

  gcc_jit_context_new_function_ptr_type
  gcc_jit_context_new_call_through_ptr

gcc/jit/
        * TODO.rst: Function ptrs are done.
        * internal-api.c
        (gcc::jit::recording::context::new_function_ptr_type): New method.
        (gcc::jit::recording::context::new_call_through_ptr): New method.
        (gcc::jit::recording::memento_of_get_pointer::make_debug_string):
        Add special-case handling of function pointer types.
        (gcc::jit::recording::function_type::make_debug_string_with_ptr):
        New method.
        (gcc::jit::recording::function_type::make_debug_string):
        Reimplement in terms of...
        (gcc::jit::recording::function_type::make_debug_string_with): New
        method, based on make_debug_string, but allowing for arbitrary
        text between the return type and the parameters.
        (gcc::jit::recording::call_through_ptr::call_through_ptr): New
        method.
        (gcc::jit::recording::call_through_ptr::replay_into): New method.
        (gcc::jit::recording::call_through_ptr::make_debug_string): New
        method.
        (gcc::jit::playback::context::new_call): Reimplement in terms of...
        (gcc::jit::playback::context::build_call): New method, using parts
        of old implementation of new_call, so that we can share this
        with...
        (gcc::jit::playback::context::new_call_through_ptr): New method.
        * internal-api.h
        (gcc::jit::recording::context::new_function_ptr_type): New method.
        (gcc::jit::recording::context::new_call_through_ptr): New method.
        (gcc::jit::recording::type::dyn_cast_function_type): New method.
        (gcc::jit::recording::function_type::dyn_cast_function_type): New
        method.
        (gcc::jit::recording::function_type::make_debug_string_with_ptr):
        New method.
        (gcc::jit::recording::function_type::make_debug_string_with): New
        method.
        (gcc::jit::recording::call_through_ptr): New subclass of rvalue.
        (gcc::jit::playback::context::new_call_through_ptr): New method.
        (gcc::jit::playback::context::build_call): New method.
        * libgccjit.c (gcc_jit_context_new_function_ptr_type): New
        function.
        (gcc_jit_context_new_call_through_ptr): New function.
        * libgccjit.h (gcc_jit_context_new_function_ptr_type): New
        function.
        (gcc_jit_context_new_call_through_ptr): New function.
        * libgccjit.map (gcc_jit_context_new_call_through_ptr): New function.
        (gcc_jit_context_new_function_ptr_type): New function.

gcc/testsuite/
        * jit.dg/test-calling-function-ptr.c: New test case.
        * jit.dg/test-combination.c: Add test-calling-function-ptr.c.
        * jit.dg/test-error-call-through-ptr-with-mismatching-args.c: New
        test case.
        * jit.dg/test-error-call-through-ptr-with-non-function.c: New test
        case.
        * jit.dg/test-error-call-through-ptr-with-non-pointer.c: New test
        case.
        * jit.dg/test-error-call-through-ptr-with-not-enough-args.c: New
        test case.
---
 gcc/jit/ChangeLog.jit                              |  47 ++++++
 gcc/jit/TODO.rst                                   |   5 -
 gcc/jit/internal-api.c                             | 176 ++++++++++++++++++---
 gcc/jit/internal-api.h                             |  46 ++++++
 gcc/jit/libgccjit.c                                | 109 +++++++++++++
 gcc/jit/libgccjit.h                                |  20 +++
 gcc/jit/libgccjit.map                              |   2 +
 gcc/testsuite/ChangeLog.jit                        |  13 ++
 gcc/testsuite/jit.dg/test-calling-function-ptr.c   | 118 ++++++++++++++
 gcc/testsuite/jit.dg/test-combination.c            |   9 ++
 ...-error-call-through-ptr-with-mismatching-args.c |  74 +++++++++
 ...test-error-call-through-ptr-with-non-function.c |  65 ++++++++
 .../test-error-call-through-ptr-with-non-pointer.c |  62 ++++++++
 ...t-error-call-through-ptr-with-not-enough-args.c |  70 ++++++++
 ...est-error-call-through-ptr-with-too-many-args.c |  87 ++++++++++
 15 files changed, 879 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-calling-function-ptr.c
 create mode 100644 
gcc/testsuite/jit.dg/test-error-call-through-ptr-with-mismatching-args.c
 create mode 100644 
gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-function.c
 create mode 100644 
gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-pointer.c
 create mode 100644 
gcc/testsuite/jit.dg/test-error-call-through-ptr-with-not-enough-args.c
 create mode 100644 
gcc/testsuite/jit.dg/test-error-call-through-ptr-with-too-many-args.c

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index 3db15e8..f7fd642 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,50 @@
+2014-08-08  David Malcolm  <dmalc...@redhat.com>
+
+       * TODO.rst: Function ptrs are done.
+       * internal-api.c
+       (gcc::jit::recording::context::new_function_ptr_type): New method.
+       (gcc::jit::recording::context::new_call_through_ptr): New method.
+       (gcc::jit::recording::memento_of_get_pointer::make_debug_string):
+       Add special-case handling of function pointer types.
+       (gcc::jit::recording::function_type::make_debug_string_with_ptr):
+       New method.
+       (gcc::jit::recording::function_type::make_debug_string):
+       Reimplement in terms of...
+       (gcc::jit::recording::function_type::make_debug_string_with): New
+       method, based on make_debug_string, but allowing for arbitrary
+       text between the return type and the parameters.
+       (gcc::jit::recording::call_through_ptr::call_through_ptr): New
+       method.
+       (gcc::jit::recording::call_through_ptr::replay_into): New method.
+       (gcc::jit::recording::call_through_ptr::make_debug_string): New
+       method.
+       (gcc::jit::playback::context::new_call): Reimplement in terms of...
+       (gcc::jit::playback::context::build_call): New method, using parts
+       of old implementation of new_call, so that we can share this
+       with...
+       (gcc::jit::playback::context::new_call_through_ptr): New method.
+       * internal-api.h
+       (gcc::jit::recording::context::new_function_ptr_type): New method.
+       (gcc::jit::recording::context::new_call_through_ptr): New method.
+       (gcc::jit::recording::type::dyn_cast_function_type): New method.
+       (gcc::jit::recording::function_type::dyn_cast_function_type): New
+       method.
+       (gcc::jit::recording::function_type::make_debug_string_with_ptr):
+       New method.
+       (gcc::jit::recording::function_type::make_debug_string_with): New
+       method.
+       (gcc::jit::recording::call_through_ptr): New subclass of rvalue.
+       (gcc::jit::playback::context::new_call_through_ptr): New method.
+       (gcc::jit::playback::context::build_call): New method.
+       * libgccjit.c (gcc_jit_context_new_function_ptr_type): New
+       function.
+       (gcc_jit_context_new_call_through_ptr): New function.
+       * libgccjit.h (gcc_jit_context_new_function_ptr_type): New
+       function.
+       (gcc_jit_context_new_call_through_ptr): New function.
+       * libgccjit.map (gcc_jit_context_new_call_through_ptr): New function.
+       (gcc_jit_context_new_function_ptr_type): New function.
+
 2014-07-25  David Malcolm  <dmalc...@redhat.com>
 
        * TODO.rst (error-checking): Remove various items that either
diff --git a/gcc/jit/TODO.rst b/gcc/jit/TODO.rst
index caf78e3..d832337 100644
--- a/gcc/jit/TODO.rst
+++ b/gcc/jit/TODO.rst
@@ -17,7 +17,6 @@ Initial Release
 
   * more types:
     * unions
-    * function ptrs
 
 * expose the statements in the API? (mostly so they can be stringified?)
 
@@ -31,10 +30,6 @@ Initial Release
 
   so you can access "static" fns in your code.
 
-* ability to call an rvalue function pointer, perhaps keeping the
-  existing API to avoid needing to build a function ptr from a
-  function.
-
 * ability to turn a function into a function pointer::
 
     gcc_jit_function_as_rvalue ()
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index 768d41c..cacb526 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -381,6 +381,24 @@ recording::context::new_struct_type (recording::location 
*loc,
   return result;
 }
 
+recording::type *
+recording::context::new_function_ptr_type (recording::location *, /* unused 
loc */
+                                          recording::type *return_type,
+                                          int num_params,
+                                          recording::type **param_types,
+                                          int is_variadic)
+{
+  recording::function_type *fn_type =
+    new function_type (this,
+                      return_type,
+                      num_params,
+                      param_types,
+                      is_variadic);
+  record (fn_type);
+
+  /* Return a pointer-type to the the function type.  */
+  return fn_type->get_pointer ();
+}
 
 recording::param *
 recording::context::new_param (recording::location *loc,
@@ -528,6 +546,17 @@ recording::context::new_call (recording::location *loc,
   return result;
 }
 
+recording::rvalue *
+recording::context::new_call_through_ptr (recording::location *loc,
+                                         recording::rvalue *fn_ptr,
+                                         int numargs,
+                                         recording::rvalue **args)
+  {
+  recording::rvalue *result = new call_through_ptr (this, loc, fn_ptr, 
numargs, args);
+  record (result);
+  return result;
+}
+
 recording::lvalue *
 recording::context::new_array_access (recording::location *loc,
                                      recording::rvalue *ptr,
@@ -1076,6 +1105,12 @@ recording::memento_of_get_pointer::replay_into (replayer 
*)
 recording::string *
 recording::memento_of_get_pointer::make_debug_string ()
 {
+  /* Special-case function pointer types, to put the "*" in parens between
+     the return type and the params (for one level of dereferencing, at
+     least).  */
+  if (function_type *fn_type = m_other_type->dyn_cast_function_type ())
+    return fn_type->make_debug_string_with_ptr ();
+
   return string::from_printf (m_ctxt,
                              "%s *", m_other_type->get_debug_string ());
 }
@@ -1173,8 +1208,20 @@ recording::function_type::replay_into (replayer *r)
 }
 
 recording::string *
+recording::function_type::make_debug_string_with_ptr ()
+{
+  return make_debug_string_with ("(*) ");
+}
+
+recording::string *
 recording::function_type::make_debug_string ()
 {
+  return make_debug_string_with ("");
+}
+
+recording::string *
+recording::function_type::make_debug_string_with (const char *insert)
+{
   /* First, build a buffer for the arguments.  */
   /* Calculate length of said buffer.  */
   size_t sz = 1; /* nil terminator */
@@ -1214,8 +1261,9 @@ recording::function_type::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)",
+                                       "%s %s(%s)",
                                        m_return_type->get_debug_string (),
+                                       insert,
                                        argbuf);
 
   delete[] argbuf;
@@ -2092,6 +2140,73 @@ recording::call::make_debug_string ()
   return result;
 }
 
+recording::call_through_ptr::call_through_ptr (recording::context *ctxt,
+                                              recording::location *loc,
+                                              recording::rvalue *fn_ptr,
+                                              int numargs,
+                                              rvalue **args)
+: rvalue (ctxt, loc,
+         fn_ptr->get_type ()->dereference ()
+           ->as_a_function_type ()->get_return_type ()),
+  m_fn_ptr (fn_ptr),
+  m_args ()
+{
+  for (int i = 0; i< numargs; i++)
+    m_args.safe_push (args[i]);
+}
+
+void
+recording::call_through_ptr::replay_into (replayer *r)
+{
+  vec<playback::rvalue *> playback_args;
+  playback_args.create (m_args.length ());
+  for (unsigned i = 0; i< m_args.length (); i++)
+    playback_args.safe_push (m_args[i]->playback_rvalue ());
+
+  set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
+                                            m_fn_ptr->playback_rvalue (),
+                                            playback_args));
+}
+
+recording::string *
+recording::call_through_ptr::make_debug_string ()
+{
+  /* First, build a buffer for the arguments.  */
+  /* Calculate length of said buffer.  */
+  size_t sz = 1; /* nil terminator */
+  for (unsigned i = 0; i< m_args.length (); i++)
+    {
+      sz += strlen (m_args[i]->get_debug_string ());
+      sz += 2; /* ", " separator */
+    }
+
+  /* Now allocate and populate the buffer.  */
+  char *argbuf = new char[sz];
+  size_t len = 0;
+
+  for (unsigned i = 0; i< m_args.length (); i++)
+    {
+      strcpy (argbuf + len, m_args[i]->get_debug_string ());
+      len += strlen (m_args[i]->get_debug_string ());
+      if (i + 1 < m_args.length ())
+       {
+         strcpy (argbuf + len, ", ");
+         len += 2;
+       }
+    }
+  argbuf[len] = '\0';
+
+  /* ...and use it to get the string for the call as a whole.  */
+  string *result = string::from_printf (m_ctxt,
+                                       "%s (%s)",
+                                       m_fn_ptr->get_debug_string (),
+                                       argbuf);
+
+  delete[] argbuf;
+
+  return result;
+}
+
 void
 recording::array_access::replay_into (replayer *r)
 {
@@ -3034,33 +3149,25 @@ new_comparison (location *loc,
 
 playback::rvalue *
 playback::context::
-new_call (location *loc,
-         function *func,
-         vec<rvalue *> args)
+build_call (location *loc,
+           tree fn_ptr,
+           vec<rvalue *> args)
 {
-  tree fndecl;
   vec<tree, va_gc> *tree_args;
-
-  gcc_assert (func);
-
-  // FIXME: type checking
-  // FIXME: check num args and types
-
-  fndecl = func->as_fndecl ();
-
   vec_alloc (tree_args, args.length ());
   for (unsigned i = 0; i < args.length (); i++)
     tree_args->quick_push (args[i]->as_tree ());
 
-  tree fntype = TREE_TYPE (fndecl);
-  tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
-
   if (loc)
-    set_tree_location (fn, 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 (func->get_return_type_as_tree (),
-                                    fn, tree_args));
+                    build_call_vec (return_type,
+                                    fn_ptr, tree_args));
 
   /* see c-typeck.c: build_function_call
      which calls build_function_call_vec
@@ -3073,6 +3180,37 @@ new_call (location *loc,
    */
 }
 
+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);
+}
+
+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);
+}
+
 tree
 playback::context::build_cast (playback::location *loc,
                               playback::rvalue *expr,
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index 331edd5..c4fe2f3 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -192,6 +192,13 @@ public:
   new_struct_type (location *loc,
                   const char *name);
 
+  type *
+  new_function_ptr_type (location *loc,
+                        type *return_type,
+                        int num_params,
+                        type **param_types,
+                        int is_variadic);
+
   param *
   new_param (location *loc,
             type *type,
@@ -253,6 +260,11 @@ public:
            int numargs, rvalue **args);
 
   rvalue *
+  new_call_through_ptr (location *loc,
+                       rvalue *fn_ptr,
+                       int numargs, rvalue **args);
+
+  rvalue *
   new_cast (location *loc,
            rvalue *expr,
            type *type_);
@@ -478,6 +490,7 @@ public:
   virtual type *dereference () = 0;
 
   /* Dynamic casts.  */
+  virtual function_type *dyn_cast_function_type () { return NULL; }
   virtual function_type *as_a_function_type() { gcc_unreachable (); return 
NULL; }
   virtual struct_ *dyn_cast_struct () { return NULL; }
 
@@ -692,6 +705,7 @@ public:
                 int is_variadic);
 
   type *dereference ();
+  function_type *dyn_cast_function_type () { return this; }
   function_type *as_a_function_type () { return this; }
 
   bool is_int () const { return false; }
@@ -706,8 +720,11 @@ public:
   vec<type *> get_param_types () const { return m_param_types; }
   int is_variadic () const { return m_is_variadic; }
 
+  string * make_debug_string_with_ptr ();
+
  private:
   string * make_debug_string ();
+  string * make_debug_string_with (const char *);
 
 private:
   type *m_return_type;
@@ -1262,6 +1279,25 @@ private:
   vec<rvalue *> m_args;
 };
 
+class call_through_ptr : public rvalue
+{
+public:
+  call_through_ptr (context *ctxt,
+                   location *loc,
+                   rvalue *fn_ptr,
+                   int numargs,
+                   rvalue **args);
+
+  void replay_into (replayer *r);
+
+private:
+  string * make_debug_string ();
+
+private:
+  rvalue *m_fn_ptr;
+  vec<rvalue *> m_args;
+};
+
 class array_access : public lvalue
 {
 public:
@@ -1704,6 +1740,11 @@ public:
            vec<rvalue *> args);
 
   rvalue *
+  new_call_through_ptr (location *loc,
+                       rvalue *fn_ptr,
+                       vec<rvalue *> args);
+
+  rvalue *
   new_cast (location *loc,
            rvalue *expr,
            type *type_);
@@ -1779,6 +1820,11 @@ public:
 private:
   void dump_generated_code ();
 
+  rvalue *
+  build_call (location *loc,
+             tree fn_ptr,
+             vec<rvalue *> args);
+
   tree
   build_cast (location *loc,
              rvalue *expr,
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index bbc1941..28deb7d 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -461,6 +461,33 @@ gcc_jit_struct_set_fields (gcc_jit_struct *struct_type,
                           (gcc::jit::recording::field **)fields);
 }
 
+gcc_jit_type *
+gcc_jit_context_new_function_ptr_type (gcc_jit_context *ctxt,
+                                      gcc_jit_location *loc,
+                                      gcc_jit_type *return_type,
+                                      int num_params,
+                                      gcc_jit_type **param_types,
+                                      int is_variadic)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (return_type, ctxt, loc, "NULL return_type");
+  RETURN_NULL_IF_FAIL (
+    (num_params == 0) || param_types,
+    ctxt, loc,
+    "NULL param_types creating function pointer type");
+  for (int i = 0; i < num_params; i++)
+    RETURN_NULL_IF_FAIL_PRINTF1 (
+      param_types[i],
+      ctxt, loc,
+      "NULL parameter type %i creating function pointer type", i);
+
+  return (gcc_jit_type*)
+    ctxt->new_function_ptr_type (loc, return_type,
+                                num_params,
+                                (gcc::jit::recording::type **)param_types,
+                                is_variadic);
+}
+
 /* Constructing functions.  */
 gcc_jit_param *
 gcc_jit_context_new_param (gcc_jit_context *ctxt,
@@ -889,6 +916,88 @@ gcc_jit_context_new_call (gcc_jit_context *ctxt,
                                           (gcc::jit::recording::rvalue 
**)args);
 }
 
+gcc_jit_rvalue *
+gcc_jit_context_new_call_through_ptr (gcc_jit_context *ctxt,
+                                     gcc_jit_location *loc,
+                                     gcc_jit_rvalue *fn_ptr,
+                                     int numargs, gcc_jit_rvalue **args)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (fn_ptr, ctxt, loc, "NULL fn_ptr");
+  if (numargs)
+    RETURN_NULL_IF_FAIL (args, ctxt, loc, "NULL args");
+
+  gcc::jit::recording::type *ptr_type = fn_ptr->get_type ()->dereference ();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    ptr_type, ctxt, loc,
+    "fn_ptr is not a ptr: %s"
+    " type: %s",
+    fn_ptr->get_debug_string (),
+    fn_ptr->get_type ()->get_debug_string ());
+
+  gcc::jit::recording::function_type *fn_type =
+    ptr_type->dyn_cast_function_type();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    fn_type, ctxt, loc,
+    "fn_ptr is not a function ptr: %s"
+    " type: %s",
+    fn_ptr->get_debug_string (),
+    fn_ptr->get_type ()->get_debug_string ());
+
+  int min_num_params = fn_type->get_param_types ().length ();
+  bool is_variadic = fn_type->is_variadic ();
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    numargs >= min_num_params,
+    ctxt, loc,
+    "not enough arguments to fn_ptr: %s"
+    " (got %i args, expected %i)",
+    fn_ptr->get_debug_string (),
+    numargs, min_num_params);
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    (numargs == min_num_params || is_variadic),
+    ctxt, loc,
+    "too many arguments to fn_ptr: %s"
+    " (got %i args, expected %i)",
+    fn_ptr->get_debug_string (),
+    numargs, min_num_params);
+
+  for (int i = 0; i < min_num_params; i++)
+    {
+      gcc::jit::recording::type *param_type = fn_type->get_param_types ()[i];
+      gcc_jit_rvalue *arg = args[i];
+
+      RETURN_NULL_IF_FAIL_PRINTF3 (
+       arg,
+       ctxt, loc,
+       "NULL argument %i to fn_ptr: %s"
+       " (type: %s)",
+       i + 1,
+       fn_ptr->get_debug_string (),
+       param_type->get_debug_string ());
+
+      RETURN_NULL_IF_FAIL_PRINTF6 (
+       compatible_types (param_type,
+                         arg->get_type ()),
+       ctxt, loc,
+       "mismatching types for argument %d of fn_ptr: %s:"
+       " assignment to param %d (type: %s) from %s (type: %s)",
+       i + 1,
+       fn_ptr->get_debug_string (),
+       i + 1,
+       param_type->get_debug_string (),
+       arg->get_debug_string (),
+       arg->get_type ()->get_debug_string ());
+    }
+
+  return (gcc_jit_rvalue *)(
+           ctxt->new_call_through_ptr (loc,
+                                       fn_ptr,
+                                       numargs,
+                                       (gcc::jit::recording::rvalue **)args));
+}
+
 static bool
 is_valid_cast (gcc::jit::recording::type *src_type,
               gcc_jit_type *dst_type)
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 3019449..a7e437ed 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -436,6 +436,16 @@ gcc_jit_struct_set_fields (gcc_jit_struct *struct_type,
                           int num_fields,
                           gcc_jit_field **fields);
 
+/* Function pointers. */
+
+extern gcc_jit_type *
+gcc_jit_context_new_function_ptr_type (gcc_jit_context *ctxt,
+                                      gcc_jit_location *loc,
+                                      gcc_jit_type *return_type,
+                                      int num_params,
+                                      gcc_jit_type **param_types,
+                                      int is_variadic);
+
 /**********************************************************************
  Constructing functions.
  **********************************************************************/
@@ -704,12 +714,22 @@ gcc_jit_context_new_comparison (gcc_jit_context *ctxt,
                                enum gcc_jit_comparison op,
                                gcc_jit_rvalue *a, gcc_jit_rvalue *b);
 
+/* Function calls.  */
+
+/* Call of a specific function.  */
 extern gcc_jit_rvalue *
 gcc_jit_context_new_call (gcc_jit_context *ctxt,
                          gcc_jit_location *loc,
                          gcc_jit_function *func,
                          int numargs , gcc_jit_rvalue **args);
 
+/* Call through a function pointer.  */
+extern gcc_jit_rvalue *
+gcc_jit_context_new_call_through_ptr (gcc_jit_context *ctxt,
+                                     gcc_jit_location *loc,
+                                     gcc_jit_rvalue *fn_ptr,
+                                     int numargs, gcc_jit_rvalue **args);
+
 /* Type-coercion.
 
    Currently only a limited set of conversions are possible:
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 0d9968c..d7f5d7c 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -23,11 +23,13 @@
     gcc_jit_context_new_array_type;
     gcc_jit_context_new_binary_op;
     gcc_jit_context_new_call;
+    gcc_jit_context_new_call_through_ptr;
     gcc_jit_context_new_cast;
     gcc_jit_context_new_child_context;
     gcc_jit_context_new_comparison;
     gcc_jit_context_new_field;
     gcc_jit_context_new_function;
+    gcc_jit_context_new_function_ptr_type;
     gcc_jit_context_new_global;
     gcc_jit_context_new_location;
     gcc_jit_context_new_opaque_struct;
diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit
index 39eed72..7fc5c4d 100644
--- a/gcc/testsuite/ChangeLog.jit
+++ b/gcc/testsuite/ChangeLog.jit
@@ -1,3 +1,16 @@
+2014-08-08  David Malcolm  <dmalc...@redhat.com>
+
+       * jit.dg/test-calling-function-ptr.c: New test case.
+       * jit.dg/test-combination.c: Add test-calling-function-ptr.c.
+       * jit.dg/test-error-call-through-ptr-with-mismatching-args.c: New
+       test case.
+       * jit.dg/test-error-call-through-ptr-with-non-function.c: New test
+       case.
+       * jit.dg/test-error-call-through-ptr-with-non-pointer.c: New test
+       case.
+       * jit.dg/test-error-call-through-ptr-with-not-enough-args.c: New
+       test case.
+
 2014-07-25  David Malcolm  <dmalc...@redhat.com>
 
        * jit.dg/test-error-index-not-a-numeric-type.c: New test case.
diff --git a/gcc/testsuite/jit.dg/test-calling-function-ptr.c 
b/gcc/testsuite/jit.dg/test-calling-function-ptr.c
new file mode 100644
index 0000000..e21bd15
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-calling-function-ptr.c
@@ -0,0 +1,118 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     test_calling_function_ptr (void (*fn_ptr) (int, int, int) fn_ptr,
+                                int a)
+     {
+        fn_ptr (a * 3, a * 4, a * 5);
+     }
+  */
+
+  int i;
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the function ptr type.  */
+  gcc_jit_type *param_types[3];
+  param_types[0] = int_type;
+  param_types[1] = int_type;
+  param_types[2] = int_type;
+
+  gcc_jit_type *fn_ptr_type =
+    gcc_jit_context_new_function_ptr_type (ctxt, NULL,
+                                          void_type,
+                                          3, param_types, 0);
+
+  /* Ensure that function ptr types have sane debug strings.  */
+
+  CHECK_STRING_VALUE (
+    gcc_jit_object_get_debug_string (gcc_jit_type_as_object (fn_ptr_type)),
+    "void (*) (int, int, int)");
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_fn_ptr =
+    gcc_jit_context_new_param (ctxt, NULL, fn_ptr_type, "fn_ptr");
+  gcc_jit_param *param_a =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "a");
+
+  gcc_jit_param *params[2];
+  params[0] = param_fn_ptr;
+  params[1] = param_a;
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_calling_function_ptr",
+                                  2, params,
+                                  0);
+  /* "a * 3, a * 4, a * 5"  */
+  gcc_jit_rvalue *args[3];
+  for (i = 0; i < 3; i++)
+    args[i] =
+      gcc_jit_context_new_binary_op (
+        ctxt, NULL,
+        GCC_JIT_BINARY_OP_MULT,
+        int_type,
+        gcc_jit_param_as_rvalue (param_a),
+        gcc_jit_context_new_rvalue_from_int (
+          ctxt,
+          int_type,
+          (i + 3) ));
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      gcc_jit_param_as_rvalue (param_fn_ptr),
+      3, args));
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+static int called_through_ptr_with[3];
+
+static void
+function_called_through_fn_ptr (int i, int j, int k)
+{
+  called_through_ptr_with[0] = i;
+  called_through_ptr_with[1] = j;
+  called_through_ptr_with[2] = k;
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef void (*fn_type) (void (*fn_ptr) (int, int, int),
+                          int);
+  CHECK_NON_NULL (result);
+
+  fn_type test_caller =
+    (fn_type)gcc_jit_result_get_code (result, "test_calling_function_ptr");
+  CHECK_NON_NULL (test_caller);
+
+  called_through_ptr_with[0] = 0;
+  called_through_ptr_with[1] = 0;
+  called_through_ptr_with[2] = 0;
+
+  /* Call the JIT-generated function.  */
+  test_caller (function_called_through_fn_ptr, 5);
+
+  /* Verify that it correctly called "function_called_through_fn_ptr".  */
+  CHECK_VALUE (called_through_ptr_with[0], 15);
+  CHECK_VALUE (called_through_ptr_with[1], 20);
+  CHECK_VALUE (called_through_ptr_with[2], 25);
+}
+
diff --git a/gcc/testsuite/jit.dg/test-combination.c 
b/gcc/testsuite/jit.dg/test-combination.c
index 6f65592..c7436c4 100644
--- a/gcc/testsuite/jit.dg/test-combination.c
+++ b/gcc/testsuite/jit.dg/test-combination.c
@@ -35,6 +35,13 @@
 #undef create_code
 #undef verify_code
 
+/* test-calling-function-ptr.c */
+#define create_code create_code_calling_function_ptr
+#define verify_code verify_code_calling_function_ptr
+#include "test-calling-function-ptr.c"
+#undef create_code
+#undef verify_code
+
 /* test-dot-product.c */
 #define create_code create_code_dot_product
 #define verify_code verify_code_dot_product
@@ -151,6 +158,7 @@ create_code (gcc_jit_context *ctxt, void * user_data)
   create_code_array_as_pointer (ctxt, user_data);
   create_code_arrays (ctxt, user_data);
   create_code_calling_external_function (ctxt, user_data);
+  create_code_calling_function_ptr (ctxt, user_data);
   create_code_dot_product (ctxt, user_data);
   create_code_expressions (ctxt, user_data);
   create_code_factorial (ctxt, user_data);
@@ -172,6 +180,7 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
   verify_code_array_as_pointer (ctxt, result);
   verify_code_arrays (ctxt, result);
   verify_code_calling_external_function (ctxt, result);
+  verify_code_calling_function_ptr (ctxt, result);
   verify_code_dot_product (ctxt, result);
   verify_code_expressions (ctxt, result);
   verify_code_factorial (ctxt, result);
diff --git 
a/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-mismatching-args.c 
b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-mismatching-args.c
new file mode 100644
index 0000000..afe5a7c
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-mismatching-args.c
@@ -0,0 +1,74 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     test_fn (void (*some_fn_ptr) (void *))
+     {
+        some_fn_ptr (42);
+     }
+
+     and verify that the API complains about the mismatching argument
+     type ("int" vs "void *").  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *void_ptr_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the function ptr type.  */
+  gcc_jit_type *fn_ptr_type =
+    gcc_jit_context_new_function_ptr_type (ctxt, NULL,
+                                          void_type,
+                                          1, &void_ptr_type, 0);
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_fn_ptr =
+    gcc_jit_context_new_param (ctxt, NULL, fn_ptr_type, "some_fn_ptr");
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_fn",
+                                  1, &param_fn_ptr,
+                                  0);
+  /* some_fn_ptr (42); */
+  gcc_jit_rvalue *arg =
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 42);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      gcc_jit_param_as_rvalue (param_fn_ptr),
+      1, &arg));
+  /* the above has the wrong type for argument 1.  */
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_call_through_ptr:"
+                      " mismatching types for argument 1 of fn_ptr:"
+                      " some_fn_ptr:"
+                      " assignment to param 1 (type: void *)"
+                      " from (int)42 (type: int)"));
+}
+
diff --git 
a/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-function.c 
b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-function.c
new file mode 100644
index 0000000..513e3d3
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-function.c
@@ -0,0 +1,65 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     test_fn (void *some_ptr)
+     {
+        ((some_unspecified_fn_ptr_type)some_ptr) (42);
+     }
+
+     and verify that the API complains about the 42 not being a
+     function pointer.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *void_ptr_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the test_fn.  */
+  gcc_jit_param *some_ptr =
+    gcc_jit_context_new_param (ctxt, NULL, void_ptr_type, "some_ptr");
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_fn",
+                                  1, &some_ptr,
+                                  0);
+  gcc_jit_rvalue *arg =
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 42);
+
+  /* ((some_unspecified_fn_ptr_type)some_ptr) (42); */
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      /* This is not a function pointer.  */
+      gcc_jit_param_as_rvalue (some_ptr),
+      1, &arg));
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_call_through_ptr:"
+                      " fn_ptr is not a function ptr: some_ptr type: void *"));
+}
+
diff --git 
a/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-pointer.c 
b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-pointer.c
new file mode 100644
index 0000000..8bb50d9
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-non-pointer.c
@@ -0,0 +1,62 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     test_fn ()
+     {
+        ((some_unspecified_fn_ptr_type)42) (43);
+     }
+
+     and verify that the API complains about the 42 not being a
+     function pointer.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the test_fn.  */
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+  gcc_jit_rvalue *not_a_function =
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 42);
+  gcc_jit_rvalue *arg =
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 43);
+
+  /* ((some_unspecified_fn_ptr_type)42) (43); */
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      /* This is not even a pointer, let alone a function pointer.  */
+      not_a_function,
+      1, &arg));
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_call_through_ptr:"
+                      " fn_ptr is not a ptr: (int)42 type: int"));
+}
+
diff --git 
a/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-not-enough-args.c 
b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-not-enough-args.c
new file mode 100644
index 0000000..3e160f4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-not-enough-args.c
@@ -0,0 +1,70 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     test_caller (void (*some_fn_ptr) (int p))
+     {
+        called_function (); // missing arg
+     }
+
+     and verify that the API complains about the missing argument.
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the function ptr type.  */
+  gcc_jit_type *fn_ptr_type =
+    gcc_jit_context_new_function_ptr_type (ctxt, NULL,
+                                          void_type,
+                                          1, &int_type, 0);
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_fn_ptr =
+    gcc_jit_context_new_param (ctxt, NULL, fn_ptr_type, "some_fn_ptr");
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_caller",
+                                  1, &param_fn_ptr,
+                                  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  /* called_function ();  */
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      gcc_jit_param_as_rvalue (param_fn_ptr),
+      0, NULL));
+  /* the above has not enough args.  */
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  /* Ensure that mismatching arg count leads to the API giving a NULL
+     result back.  */
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_call_through_ptr:"
+                      " not enough arguments to fn_ptr: some_fn_ptr"
+                      " (got 0 args, expected 1)"));
+}
+
diff --git 
a/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-too-many-args.c 
b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-too-many-args.c
new file mode 100644
index 0000000..b8cb2f4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-call-through-ptr-with-too-many-args.c
@@ -0,0 +1,87 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void
+  called_function (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+     void
+     test_caller (void (*some_fn_ptr) (void), int a)
+     {
+        some_fn_ptr (a);
+     }
+
+     and verify that the API complains about the mismatching arg
+     counts.
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Build the function ptr type.  */
+  gcc_jit_type *fn_ptr_type =
+    gcc_jit_context_new_function_ptr_type (ctxt, NULL,
+                                          void_type,
+                                          0, NULL, 0);
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_fn_ptr =
+    gcc_jit_context_new_param (ctxt, NULL, fn_ptr_type, "some_fn_ptr");
+  gcc_jit_param *param_a =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "a");
+  gcc_jit_param *params[2];
+  params[0] = param_fn_ptr;
+  params[1] = param_a;
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_type,
+                                  "test_caller",
+                                  2, params,
+                                  0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+
+  /* some_fn_ptr (a); */
+  gcc_jit_rvalue *arg = gcc_jit_param_as_rvalue (param_a);
+  gcc_jit_block_add_eval (
+    block, NULL,
+    gcc_jit_context_new_call_through_ptr (
+      ctxt,
+      NULL,
+      gcc_jit_param_as_rvalue (param_fn_ptr),
+      1, &arg));
+  /* the above has too many args.  */
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  /* Ensure that mismatching arg count leads to the API giving a NULL
+     result back.  */
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     "gcc_jit_context_new_call_through_ptr:"
+                     " too many arguments to fn_ptr:"
+                     " some_fn_ptr (got 1 args, expected 0)");
+}
+
-- 
1.8.5.3

Reply via email to