@@ -459,6 +459,15 @@ Function calls
/* Add "(void)printf (arg0, arg1);". */
block.add_eval (ctxt.new_call (printf_func, arg0, arg1));
+Function pointers
+*****************
+
+.. function:: gccjit::rvalue \
+ gccjit::function::get_address (gccjit::location loc)
+
+ Get the address of a function as an rvalue, of function pointer
+ type.
+
Type-coercion
*************
@@ -156,3 +156,10 @@ entrypoints:
-------------------
``LIBGCCJIT_ABI_8`` covers the addition of
:func:`gcc_jit_type_get_vector`
+
+.. _LIBGCCJIT_ABI_9:
+
+``LIBGCCJIT_ABI_9``
+-------------------
+``LIBGCCJIT_ABI_9`` covers the addition of
+:func:`gcc_jit_function_get_address`
@@ -449,6 +449,23 @@ Function calls
#ifdef LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call
+Function pointers
+*****************
+
+.. function:: gcc_jit_rvalue *\
+ gcc_jit_function_get_address (gcc_jit_function *fn,\
+ gcc_jit_location *loc)
+
+ Get the address of a function as an rvalue, of function pointer
+ type.
+
+ This entrypoint was added in :ref:`LIBGCCJIT_ABI_9`; you can test
+ for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_function_get_address
+
Type-coercion
*************
@@ -127,6 +127,7 @@ namespace recording {
class global;
class param;
class base_call;
+ class function_pointer;
class statement;
class case_;
@@ -1356,6 +1356,20 @@ new_block (const char *name)
return result;
}
+/* Construct a playback::rvalue instance wrapping an ADDR_EXPR for
+ this playback::function. */
+
+playback::rvalue *
+playback::function::get_address (location *loc)
+{
+ tree t_fndecl = as_fndecl ();
+ tree t_fntype = TREE_TYPE (t_fndecl);
+ tree t_fnptr = build1 (ADDR_EXPR, build_pointer_type (t_fntype), t_fndecl);
+ if (loc)
+ m_ctxt->set_tree_location (t_fnptr, loc);
+ return new rvalue (m_ctxt, t_fnptr);
+}
+
/* Build a statement list for the function as a whole out of the
lists of statements for the individual blocks, building labels
for each block. */
@@ -443,6 +443,9 @@ public:
block*
new_block (const char *name);
+ rvalue *
+ get_address (location *loc);
+
void
build_stmt_list ();
@@ -3452,7 +3452,8 @@ recording::function::function (context *ctxt,
m_is_variadic (is_variadic),
m_builtin_id (builtin_id),
m_locals (),
- m_blocks ()
+ m_blocks (),
+ m_fn_ptr_type (NULL)
{
for (int i = 0; i< num_params; i++)
{
@@ -3725,6 +3726,35 @@ recording::function::dump_to_dot (const char *path)
fclose (fp);
}
+/* Implements the post-error-checking part of
+ gcc_jit_function_get_address. */
+
+recording::rvalue *
+recording::function::get_address (recording::location *loc)
+{
+ /* Lazily create and cache the function pointer type. */
+ if (!m_fn_ptr_type)
+ {
+ /* Make a recording::function_type for this function. */
+ auto_vec <recording::type *> param_types (m_params.length ());
+ unsigned i;
+ recording::param *param;
+ FOR_EACH_VEC_ELT (m_params, i, param)
+ param_types.safe_push (param->get_type ());
+ recording::function_type *fn_type
+ = m_ctxt->new_function_type (m_return_type,
+ m_params.length (),
+ param_types.address (),
+ m_is_variadic);
+ m_fn_ptr_type = fn_type->get_pointer ();
+ }
+ gcc_assert (m_fn_ptr_type);
+
+ rvalue *result = new function_pointer (get_context (), loc, this, m_fn_ptr_type);
+ m_ctxt->record (result);
+ return result;
+}
+
/* Implementation of recording::memento::make_debug_string for
functions. */
@@ -5400,6 +5430,51 @@ recording::get_address_of_lvalue::write_reproducer (reproducer &r)
r.get_identifier (m_loc));
}
+/* The implementation of class gcc::jit::recording::function_pointer. */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+ for recording::function_pointer. */
+
+void
+recording::function_pointer::replay_into (replayer *r)
+{
+ set_playback_obj (
+ m_fn->playback_function ()->
+ get_address (playback_location (r, m_loc)));
+}
+
+void
+recording::function_pointer::visit_children (rvalue_visitor *)
+{
+ /* Empty. */
+}
+
+/* Implementation of recording::memento::make_debug_string for
+ getting the address of an lvalue. */
+
+recording::string *
+recording::function_pointer::make_debug_string ()
+{
+ return string::from_printf (m_ctxt,
+ "%s",
+ m_fn->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+ function_pointer. */
+
+void
+recording::function_pointer::write_reproducer (reproducer &r)
+{
+ const char *id = r.make_identifier (this, "address_of");
+ r.write (" gcc_jit_rvalue *%s =\n"
+ " gcc_jit_function_get_address (%s, /* gcc_jit_function *fn */\n"
+ " %s); /* gcc_jit_location *loc */\n",
+ id,
+ r.get_identifier (m_fn),
+ r.get_identifier (m_loc));
+}
+
/* The implementation of class gcc::jit::recording::local. */
/* Implementation of pure virtual hook recording::memento::replay_into
@@ -1145,6 +1145,8 @@ public:
void dump_to_dot (const char *path);
+ rvalue *get_address (location *loc);
+
private:
string * make_debug_string () FINAL OVERRIDE;
void write_reproducer (reproducer &r) FINAL OVERRIDE;
@@ -1159,6 +1161,7 @@ private:
enum built_in_function m_builtin_id;
auto_vec<local *> m_locals;
auto_vec<block *> m_blocks;
+ type *m_fn_ptr_type;
};
class block : public memento
@@ -1699,6 +1702,32 @@ private:
lvalue *m_lvalue;
};
+class function_pointer : public rvalue
+{
+public:
+ function_pointer (context *ctxt,
+ location *loc,
+ function *fn,
+ type *type)
+ : rvalue (ctxt, loc, type),
+ m_fn (fn) {}
+
+ void replay_into (replayer *r) FINAL OVERRIDE;
+
+ void visit_children (rvalue_visitor *v) FINAL OVERRIDE;
+
+private:
+ string * make_debug_string () FINAL OVERRIDE;
+ void write_reproducer (reproducer &r) FINAL OVERRIDE;
+ enum precedence get_precedence () const FINAL OVERRIDE
+ {
+ return PRECEDENCE_UNARY;
+ }
+
+private:
+ function *m_fn;
+};
+
class local : public lvalue
{
public:
@@ -368,6 +368,8 @@ namespace gccjit
const std::string &name,
location loc = location ());
+ rvalue get_address (location loc = location ());
+
/* A series of overloaded operator () with various numbers of arguments
for a very terse way of creating a call to this function. The call
is created within the same context as the function itself, which may
@@ -1392,6 +1394,13 @@ function::new_local (type type_,
name.c_str ()));
}
+inline rvalue
+function::get_address (location loc)
+{
+ return rvalue (gcc_jit_function_get_address (get_inner_function (),
+ loc.get_inner_location ()));
+}
+
inline function
block::get_function () const
{
@@ -3022,3 +3022,23 @@ gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units)
return (gcc_jit_type *)type->get_vector (num_units);
}
+
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
+ gcc::jit::recording::function::get_address method, in
+ jit-recording.c. */
+
+gcc_jit_rvalue *
+gcc_jit_function_get_address (gcc_jit_function *fn,
+ gcc_jit_location *loc)
+{
+ RETURN_NULL_IF_FAIL (fn, NULL, NULL, "NULL function");
+
+ gcc::jit::recording::context *ctxt = fn->m_ctxt;
+
+ JIT_LOG_FUNC (ctxt->get_logger ());
+ /* LOC can be NULL. */
+
+ return (gcc_jit_rvalue *)fn->get_address (loc);
+}
@@ -1418,6 +1418,21 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
extern gcc_jit_type *
gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units);
+
+#define LIBGCCJIT_HAVE_gcc_jit_function_get_address
+
+/* Get the address of a function as an rvalue, of function pointer
+ type.
+
+ This API entrypoint was added in LIBGCCJIT_ABI_9; you can test for its
+ presence using
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_function_get_address
+*/
+extern gcc_jit_rvalue *
+gcc_jit_function_get_address (gcc_jit_function *fn,
+ gcc_jit_location *loc);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -160,3 +160,8 @@ LIBGCCJIT_ABI_8 {
global:
gcc_jit_type_get_vector;
} LIBGCCJIT_ABI_7;
+
+LIBGCCJIT_ABI_9 {
+ global:
+ gcc_jit_function_get_address;
+} LIBGCCJIT_ABI_8;
@@ -71,6 +71,13 @@
#undef create_code
#undef verify_code
+/* test-returning-function-ptr.c */
+#define create_code create_code_calling_internal_function
+#define verify_code verify_code_calling_internal_function
+#include "test-returning-function-ptr.c"
+#undef create_code
+#undef verify_code
+
/* test-compound-assignment.c */
#define create_code create_code_compound_assignment
#define verify_code verify_code_compound_assignment
@@ -283,6 +290,9 @@ const struct testcase testcases[] = {
{"calling_function_ptr",
create_code_calling_function_ptr,
verify_code_calling_function_ptr},
+ {"calling_internal_function",
+ create_code_calling_internal_function,
+ verify_code_calling_internal_function},
{"compound_assignment",
create_code_compound_assignment,
verify_code_compound_assignment},
new file mode 100644
@@ -0,0 +1,143 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern void
+ internally_called_function (int i, int j, int k);
+
+#ifdef __cplusplus
+}
+#endif
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ /* Let's try to inject the equivalent of:
+ extern void internally_called_function (int i, int j, int k);
+
+ static void
+ internal_test_caller (int a)
+ {
+ internally_called_function (a * 3, a * 4, a * 5);
+ }
+
+ void (*) (int)
+ get_test_caller (void)
+ {
+ return internal_test_caller;
+ }
+ */
+ 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);
+
+ /* Declare the imported function. */
+ gcc_jit_param *params[3];
+ params[0] =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+ params[1] =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "j");
+ params[2] =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "k");
+ gcc_jit_function *called_fn =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_IMPORTED,
+ void_type,
+ "internally_called_function",
+ 3, params,
+ 0);
+
+ /* Build the test_caller fn. */
+ gcc_jit_param *param_a =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "a");
+ gcc_jit_function *test_caller =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ void_type,
+ "internal_test_caller",
+ 1, ¶m_a,
+ 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_caller, NULL);
+ gcc_jit_block_add_eval (
+ block, NULL,
+ gcc_jit_context_new_call (ctxt,
+ NULL,
+ called_fn,
+ 3, args));
+ gcc_jit_block_end_with_void_return (block, NULL);
+
+ gcc_jit_rvalue *fn_ptr
+ = gcc_jit_function_get_address (test_caller, NULL);
+
+ gcc_jit_type *fn_ptr_type
+ = gcc_jit_rvalue_get_type (fn_ptr);
+
+ /* Build the get_test_caller fn. */
+ gcc_jit_function *get_test_caller =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ fn_ptr_type,
+ "get_test_caller",
+ 0, NULL,
+ 0);
+ block = gcc_jit_function_new_block (get_test_caller, NULL);
+ gcc_jit_block_end_with_return (block, NULL, fn_ptr);
+}
+
+static int called_with[3];
+
+extern void
+internally_called_function (int i, int j, int k)
+{
+ called_with[0] = i;
+ called_with[1] = j;
+ called_with[2] = k;
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ typedef void (*test_caller_type) (int);
+ typedef test_caller_type (*get_test_caller_type) (void);
+ CHECK_NON_NULL (result);
+
+ get_test_caller_type get_test_caller =
+ (get_test_caller_type)gcc_jit_result_get_code (result, "get_test_caller");
+ CHECK_NON_NULL (get_test_caller);
+
+ test_caller_type test_caller = (test_caller_type)get_test_caller ();
+ CHECK_NON_NULL (test_caller);
+
+ called_with[0] = 0;
+ called_with[1] = 0;
+ called_with[2] = 0;
+
+ /* Call the JIT-generated function. */
+ test_caller (5);
+
+ /* Verify that it correctly called "internally_called_function". */
+ CHECK_VALUE (called_with[0], 15);
+ CHECK_VALUE (called_with[1], 20);
+ CHECK_VALUE (called_with[2], 25);
+}