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, ¶m_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, ¶m_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