+2016-05-20 David Malcolm <dmalcolm@redhat.com>
+
+ * docs/topics/compatibility.rst: Add LIBGCCJIT_ABI_6.
+ * docs/topics/expressions.rst (Function calls): Add documentation
+ of gcc_jit_rvalue_set_bool_require_tail_call.
+ * docs/_build/texinfo/libgccjit.texi: Regenerate.
+ * jit-common.h (gcc::jit::recording::base_call): Add forward decl.
+ * jit-playback.c: Within namespace gcc::jit::playback...
+ (context::build_call) Add "require_tail_call" param and use it
+ to set CALL_EXPR_MUST_TAIL_CALL.
+ (context::new_call): Add "require_tail_call" param.
+ (context::new_call_through_ptr): Likewise.
+ * jit-playback.h: Within namespace gcc::jit::playback...
+ (context::new_call: Add "require_tail_call" param.
+ (context::new_call_through_ptr): Likewise.
+ (context::build_call): Likewise.
+ * jit-recording.c: Within namespace gcc::jit::recording...
+ (base_call::base_call): New constructor.
+ (base_call::write_reproducer_tail_call): New method.
+ (call::call): Update for inheritance from base_call.
+ (call::replay_into): Provide m_require_tail_call to call
+ to new_call.
+ (call::write_reproducer): Call write_reproducer_tail_call.
+ (call_through_ptr::call_through_ptr): Update for inheritance from
+ base_call.
+ (call_through_ptr::replay_into): Provide m_require_tail_call to call
+ to new_call_through_ptr.
+ (recording::call_through_ptr::write_reproducer): Call
+ write_reproducer_tail_call.
+ * jit-recording.h: Within namespace gcc::jit::recording...
+ (rvalue::dyn_cast_base_call): New virtual function.
+ (class base_call): New subclass of class rvalue.
+ (class call): Inherit from base_call rather than directly from
+ rvalue, moving get_precedence and m_args to base_call.
+ (class call_through_ptr): Likewise.
+ * libgccjit.c (gcc_jit_rvalue_set_bool_require_tail_call): New
+ function.
+ * libgccjit.h
+ (LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call): New
+ macro.
+ (gcc_jit_rvalue_set_bool_require_tail_call): New function.
+ * libgccjit.map (LIBGCCJIT_ABI_6): New.
+ (gcc_jit_rvalue_set_bool_require_tail_call): Add.
+
2016-05-17 David Malcolm <dmalcolm@redhat.com>
* dummy-frontend.c: Include diagnostic.h.
-------------------
``LIBGCCJIT_ABI_5`` covers the addition of
:func:`gcc_jit_context_set_bool_use_external_driver`
+
+.. _LIBGCCJIT_ABI_6:
+
+``LIBGCCJIT_ABI_6``
+-------------------
+``LIBGCCJIT_ABI_6`` covers the addition of
+:func:`gcc_jit_rvalue_set_bool_require_tail_call`
The same caveat as for :c:func:`gcc_jit_context_new_call` applies.
+.. function:: void\
+ gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *call,\
+ int require_tail_call)
+
+ Given an :c:type:`gcc_jit_rvalue *` for a call created through
+ :c:func:`gcc_jit_context_new_call` or
+ :c:func:`gcc_jit_context_new_call_through_ptr`, mark/clear the
+ call as needing tail-call optimization. The optimizer will
+ attempt to optimize the call into a jump instruction; if it is
+ unable to do do, an error will be emitted.
+
+ This may be useful when implementing functions that use the
+ continuation-passing style (e.g. for functional programming
+ languages), in which every function "returns" by calling a
+ "continuation" function pointer. This call must be
+ guaranteed to be implemented as a jump, otherwise the program
+ could consume an arbitrary amount of stack space as it executed.
+
+ This entrypoint was added in :ref:`LIBGCCJIT_ABI_6`; you can test for
+ its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call
Type-coercion
*************
class local;
class global;
class param;
+ class base_call;
class statement;
class case_;
playback::context::
build_call (location *loc,
tree fn_ptr,
- const auto_vec<rvalue *> *args)
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call)
{
vec<tree, va_gc> *tree_args;
vec_alloc (tree_args, args->length ());
tree fn_type = TREE_TYPE (fn);
tree return_type = TREE_TYPE (fn_type);
- return new rvalue (this,
- build_call_vec (return_type,
- fn_ptr, tree_args));
+ tree call = build_call_vec (return_type,
+ fn_ptr, tree_args);
+
+ if (require_tail_call)
+ CALL_EXPR_MUST_TAIL_CALL (call) = 1;
+
+ return new rvalue (this, call);
/* see c-typeck.c: build_function_call
which calls build_function_call_vec
playback::context::
new_call (location *loc,
function *func,
- const auto_vec<rvalue *> *args)
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call)
{
tree fndecl;
tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
- return build_call (loc, fn, args);
+ return build_call (loc, fn, args, require_tail_call);
}
/* Construct a playback::rvalue instance (wrapping a tree) for a
playback::context::
new_call_through_ptr (location *loc,
rvalue *fn_ptr,
- const auto_vec<rvalue *> *args)
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call)
{
gcc_assert (fn_ptr);
tree t_fn_ptr = fn_ptr->as_tree ();
- return build_call (loc, t_fn_ptr, args);
+ return build_call (loc, t_fn_ptr, args, require_tail_call);
}
/* Construct a tree for a cast. */
rvalue *
new_call (location *loc,
function *func,
- const auto_vec<rvalue *> *args);
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call);
rvalue *
new_call_through_ptr (location *loc,
rvalue *fn_ptr,
- const auto_vec<rvalue *> *args);
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call);
rvalue *
new_cast (location *loc,
rvalue *
build_call (location *loc,
tree fn_ptr,
- const auto_vec<rvalue *> *args);
+ const auto_vec<rvalue *> *args,
+ bool require_tail_call);
tree
build_cast (location *loc,
r.get_identifier_as_type (get_type ()));
}
+/* The implementation of class gcc::jit::recording::base_call. */
+
+/* The constructor for gcc::jit::recording::base_call. */
+
+recording::base_call::base_call (context *ctxt,
+ location *loc,
+ type *type_,
+ int numargs,
+ rvalue **args)
+: rvalue (ctxt, loc, type_),
+ m_args (),
+ m_require_tail_call (0)
+{
+ for (int i = 0; i< numargs; i++)
+ m_args.safe_push (args[i]);
+}
+
+/* Subroutine for use by call and call_though_ptr's write_reproducer
+ methods. */
+
+void
+recording::base_call::write_reproducer_tail_call (reproducer &r,
+ const char *id)
+{
+ if (m_require_tail_call)
+ {
+ r.write (" gcc_jit_rvalue_set_bool_require_tail_call (%s, /* gcc_jit_rvalue *call*/\n"
+ " %i); /* int require_tail_call*/\n",
+ id,
+ 1);
+ }
+}
+
/* The implementation of class gcc::jit::recording::call. */
/* The constructor for gcc::jit::recording::call. */
recording::function *func,
int numargs,
rvalue **args)
-: rvalue (ctxt, loc, func->get_return_type ()),
- m_func (func),
- m_args ()
+: base_call (ctxt, loc, func->get_return_type (), numargs, args),
+ m_func (func)
{
- for (int i = 0; i< numargs; i++)
- m_args.safe_push (args[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
set_playback_obj (r->new_call (playback_location (r, m_loc),
m_func->playback_function (),
- &playback_args));
+ &playback_args,
+ m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
r.get_identifier (m_func),
m_args.length (),
args_id);
+ write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::call_through_ptr. */
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 ()
+: base_call (ctxt, loc,
+ fn_ptr->get_type ()->dereference ()
+ ->as_a_function_type ()->get_return_type (),
+ numargs, args),
+ m_fn_ptr (fn_ptr)
{
- for (int i = 0; i< numargs; i++)
- m_args.safe_push (args[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
m_fn_ptr->playback_rvalue (),
- &playback_args));
+ &playback_args,
+ m_require_tail_call));
}
/* Implementation of pure virtual hook recording::rvalue::visit_children
r.get_identifier_as_rvalue (m_fn_ptr),
m_args.length (),
args_id);
+ write_reproducer_tail_call (r, id);
}
/* The implementation of class gcc::jit::recording::array_access. */
void set_scope (function *scope);
function *get_scope () const { return m_scope; }
- /* Dynamic cast. */
+ /* Dynamic casts. */
virtual param *dyn_cast_param () { return NULL; }
+ virtual base_call *dyn_cast_base_call () { return NULL; }
virtual const char *access_as_rvalue (reproducer &r);
rvalue *m_rvalue;
};
-class call : public rvalue
+class base_call : public rvalue
+{
+ public:
+ base_call (context *ctxt,
+ location *loc,
+ type *type_,
+ int numargs,
+ rvalue **args);
+
+ enum precedence get_precedence () const FINAL OVERRIDE
+ {
+ return PRECEDENCE_POSTFIX;
+ }
+
+ base_call *dyn_cast_base_call () FINAL OVERRIDE { return this; }
+
+ void set_require_tail_call (bool require_tail_call)
+ {
+ m_require_tail_call = require_tail_call;
+ }
+
+ protected:
+ void write_reproducer_tail_call (reproducer &r, const char *id);
+
+ protected:
+ auto_vec<rvalue *> m_args;
+ bool m_require_tail_call;
+};
+
+class call : public base_call
{
public:
call (context *ctxt,
private:
string * make_debug_string () FINAL OVERRIDE;
void write_reproducer (reproducer &r) FINAL OVERRIDE;
- enum precedence get_precedence () const FINAL OVERRIDE
- {
- return PRECEDENCE_POSTFIX;
- }
private:
function *m_func;
- auto_vec<rvalue *> m_args;
};
-class call_through_ptr : public rvalue
+class call_through_ptr : public base_call
{
public:
call_through_ptr (context *ctxt,
private:
string * make_debug_string () FINAL OVERRIDE;
void write_reproducer (reproducer &r) FINAL OVERRIDE;
- enum precedence get_precedence () const FINAL OVERRIDE
- {
- return PRECEDENCE_POSTFIX;
- }
private:
rvalue *m_fn_ptr;
- auto_vec<rvalue *> m_args;
};
class array_access : public lvalue
timer->start (TV_TOTAL);
timer->push (TV_JIT_CLIENT_CODE);
}
+
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is effectively done by the
+ gcc::jit::base_call::set_require_tail_call setter in jit-recording.h. */
+
+void
+gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *rvalue,
+ int require_tail_call)
+{
+ RETURN_IF_FAIL (rvalue, NULL, NULL, "NULL call");
+ JIT_LOG_FUNC (rvalue->get_context ()->get_logger ());
+
+ /* Verify that it's a call. */
+ gcc::jit::recording::base_call *call = rvalue->dyn_cast_base_call ();
+ RETURN_IF_FAIL_PRINTF1 (call, NULL, NULL, "not a call: %s",
+ rvalue->get_debug_string ());
+
+ call->set_require_tail_call (require_tail_call);
+}
gcc_jit_timer_print (gcc_jit_timer *timer,
FILE *f_out);
+
+#define LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call
+
+/* Mark/clear a call as needing tail-call optimization.
+
+ This API entrypoint was added in LIBGCCJIT_ABI_6; you can test for its
+ presence using
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call
+*/
+extern void
+gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *call,
+ int require_tail_call);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
global:
gcc_jit_context_set_bool_use_external_driver;
} LIBGCCJIT_ABI_4;
+
+LIBGCCJIT_ABI_6 {
+ global:
+ gcc_jit_rvalue_set_bool_require_tail_call;
+} LIBGCCJIT_ABI_5;
+2016-05-20 David Malcolm <dmalcolm@redhat.com>
+
+ * jit.dg/all-non-failing-tests.h: Add
+ test-factorial-must-tail-call.c.
+ * jit.dg/test-error-impossible-must-tail-call.c: New test case.
+ * jit.dg/test-factorial-must-tail-call.c: New test case.
+
2016-05-20 Jakub Jelinek <jakub@redhat.com>
PR fortran/71204
#undef create_code
#undef verify_code
+/* test-factorial-must-tail-call.c */
+#define create_code create_code_factorial_must_tail_call
+#define verify_code verify_code_factorial_must_tail_call
+#include "test-factorial-must-tail-call.c"
+#undef create_code
+#undef verify_code
+
/* test-fibonacci.c */
#define create_code create_code_fibonacci
#define verify_code verify_code_fibonacci
{"factorial",
create_code_factorial,
verify_code_factorial},
+ {"factorial_must_tail_call",
+ create_code_factorial_must_tail_call,
+ verify_code_factorial_must_tail_call},
{"fibonacci",
create_code_fibonacci,
verify_code_fibonacci},
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ struct box { char dummy[64]; int i; };
+
+ extern struct box
+ returns_struct (int i);
+
+#ifdef __cplusplus
+}
+#endif
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ /* Let's try to inject the equivalent of:
+
+ int test (int i)
+ {
+ return [MUST TAIL CALL] returns_struct (i).i;
+ }
+
+ and verify that we get a sane error when the tail call
+ optimization can't be done. */
+
+ gcc_jit_type *char_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR);
+ gcc_jit_type *int_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+ /* Declare "struct box. */
+ gcc_jit_type *array_type =
+ gcc_jit_context_new_array_type (ctxt, NULL, char_type, 64);
+ gcc_jit_field *field_dummy =
+ gcc_jit_context_new_field (ctxt, NULL, array_type, "dummy");
+ gcc_jit_field *field_i =
+ gcc_jit_context_new_field (ctxt, NULL, int_type, "i");
+ gcc_jit_field *fields[2] = {field_dummy, field_i};
+ gcc_jit_struct *struct_box =
+ gcc_jit_context_new_struct_type (ctxt, NULL, "box", 2, fields);
+
+ /* Declare the imported function. */
+ gcc_jit_param *called_fn_param_i =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+ gcc_jit_function *called_fn =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_IMPORTED,
+ gcc_jit_struct_as_type (struct_box),
+ "called_function",
+ 1, &called_fn_param_i,
+ 0);
+
+ /* Build the test_fn. */
+ gcc_jit_param *caller_param_i =
+ gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+ gcc_jit_function *test_fn =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ int_type,
+ "test_caller",
+ 1, &caller_param_i,
+ 0);
+ gcc_jit_rvalue *arg = gcc_jit_param_as_rvalue (caller_param_i);
+
+ gcc_jit_rvalue *call =
+ gcc_jit_context_new_call (ctxt, NULL,
+ called_fn,
+ 1, &arg);
+
+ /* Mark the call as requiring tail-call optimization. */
+ gcc_jit_rvalue_set_bool_require_tail_call (call, 1);
+
+ gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+ gcc_jit_block_end_with_return (block, NULL,
+ gcc_jit_rvalue_access_field (call, NULL, field_i));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ CHECK_VALUE (result, NULL);
+
+ CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+ "cannot tail-call: callee returns a structure");
+}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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:
+
+ int
+ my_factorial_must_tail_call (int x)
+ {
+ if (x < 2)
+ return x;
+ else
+ return x * my_factorial_must_tail_call (x - 1);
+ }
+
+ and mark the call as requiring tail-call-optimization.
+ */
+ gcc_jit_type *the_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+ gcc_jit_type *return_type = the_type;
+
+ gcc_jit_param *x =
+ gcc_jit_context_new_param (ctxt, NULL, the_type, "x");
+ gcc_jit_param *params[1] = {x};
+ gcc_jit_function *func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ return_type,
+ "my_factorial_must_tail_call",
+ 1, params, 0);
+
+ gcc_jit_block *initial =
+ gcc_jit_function_new_block (func, "initial");
+ gcc_jit_block *on_true =
+ gcc_jit_function_new_block (func, "on_true");
+ gcc_jit_block *on_false =
+ gcc_jit_function_new_block (func, "on_false");
+
+ /* if (x < 2) */
+ gcc_jit_block_end_with_conditional (
+ initial, NULL,
+ gcc_jit_context_new_comparison (
+ ctxt, NULL,
+ GCC_JIT_COMPARISON_LT,
+ gcc_jit_param_as_rvalue (x),
+ gcc_jit_context_new_rvalue_from_int (
+ ctxt,
+ the_type,
+ 2)),
+ on_true,
+ on_false);
+
+ /* true branch: */
+ /* return x */
+ gcc_jit_block_end_with_return (
+ on_true,
+ NULL,
+ gcc_jit_param_as_rvalue (x));
+
+ /* false branch: */
+ gcc_jit_rvalue *x_minus_1 =
+ gcc_jit_context_new_binary_op (
+ ctxt, NULL,
+ GCC_JIT_BINARY_OP_MINUS, the_type,
+ gcc_jit_param_as_rvalue (x),
+ gcc_jit_context_new_rvalue_from_int (
+ ctxt,
+ the_type,
+ 1));
+ /* my_factorial_must_tail_call (x - 1) */
+ gcc_jit_rvalue *call =
+ gcc_jit_context_new_call (
+ ctxt, NULL,
+ func,
+ 1, &x_minus_1);
+
+ /* Mark the call as requiring tail-call optimization. */
+ gcc_jit_rvalue_set_bool_require_tail_call (call, 1);
+
+ gcc_jit_block_end_with_return (
+ on_false,
+ NULL,
+ gcc_jit_context_new_binary_op (
+ ctxt, NULL,
+ GCC_JIT_BINARY_OP_MULT, the_type,
+ gcc_jit_param_as_rvalue (x),
+ call));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ typedef int (*my_factorial_fn_type) (int);
+ CHECK_NON_NULL (result);
+ my_factorial_fn_type my_factorial_must_tail_call =
+ (my_factorial_fn_type)gcc_jit_result_get_code (result, "my_factorial_must_tail_call");
+ CHECK_NON_NULL (my_factorial_must_tail_call);
+ int val = my_factorial_must_tail_call (10);
+ note ("my_factorial_must_tail_call returned: %d", val);
+ CHECK_VALUE (val, 3628800);
+}
+