[jit] Implement function pointers

David Malcolm dmalcolm@redhat.com
Fri Aug 8 19:17:00 GMT 2014


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  <dmalcolm@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  <dmalcolm@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  <dmalcolm@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  <dmalcolm@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



More information about the Gcc-patches mailing list