mirror of git://gcc.gnu.org/git/gcc.git
jit: implement gcc_jit_rvalue_set_bool_require_tail_call
This implements the libgccjit support for must-tail-call via a new: gcc_jit_rvalue_set_bool_require_tail_call API entrypoint. (I didn't implement a wrapper for this within the C++ bindings) gcc/jit/ChangeLog: * 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. gcc/testsuite/ChangeLog: * 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. From-SVN: r236531
This commit is contained in:
parent
dfbdde160f
commit
15c671a79c
|
|
@ -1,3 +1,47 @@
|
||||||
|
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>
|
2016-05-17 David Malcolm <dmalcolm@redhat.com>
|
||||||
|
|
||||||
* dummy-frontend.c: Include diagnostic.h.
|
* dummy-frontend.c: Include diagnostic.h.
|
||||||
|
|
|
||||||
|
|
@ -135,3 +135,10 @@ entrypoints:
|
||||||
-------------------
|
-------------------
|
||||||
``LIBGCCJIT_ABI_5`` covers the addition of
|
``LIBGCCJIT_ABI_5`` covers the addition of
|
||||||
:func:`gcc_jit_context_set_bool_use_external_driver`
|
: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`
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,30 @@ Function calls
|
||||||
|
|
||||||
The same caveat as for :c:func:`gcc_jit_context_new_call` applies.
|
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
|
Type-coercion
|
||||||
*************
|
*************
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ namespace recording {
|
||||||
class local;
|
class local;
|
||||||
class global;
|
class global;
|
||||||
class param;
|
class param;
|
||||||
|
class base_call;
|
||||||
class statement;
|
class statement;
|
||||||
class case_;
|
class case_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -854,7 +854,8 @@ playback::rvalue *
|
||||||
playback::context::
|
playback::context::
|
||||||
build_call (location *loc,
|
build_call (location *loc,
|
||||||
tree fn_ptr,
|
tree fn_ptr,
|
||||||
const auto_vec<rvalue *> *args)
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call)
|
||||||
{
|
{
|
||||||
vec<tree, va_gc> *tree_args;
|
vec<tree, va_gc> *tree_args;
|
||||||
vec_alloc (tree_args, args->length ());
|
vec_alloc (tree_args, args->length ());
|
||||||
|
|
@ -868,9 +869,13 @@ build_call (location *loc,
|
||||||
tree fn_type = TREE_TYPE (fn);
|
tree fn_type = TREE_TYPE (fn);
|
||||||
tree return_type = TREE_TYPE (fn_type);
|
tree return_type = TREE_TYPE (fn_type);
|
||||||
|
|
||||||
return new rvalue (this,
|
tree call = build_call_vec (return_type,
|
||||||
build_call_vec (return_type,
|
fn_ptr, tree_args);
|
||||||
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
|
/* see c-typeck.c: build_function_call
|
||||||
which calls build_function_call_vec
|
which calls build_function_call_vec
|
||||||
|
|
@ -890,7 +895,8 @@ playback::rvalue *
|
||||||
playback::context::
|
playback::context::
|
||||||
new_call (location *loc,
|
new_call (location *loc,
|
||||||
function *func,
|
function *func,
|
||||||
const auto_vec<rvalue *> *args)
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call)
|
||||||
{
|
{
|
||||||
tree fndecl;
|
tree fndecl;
|
||||||
|
|
||||||
|
|
@ -902,7 +908,7 @@ new_call (location *loc,
|
||||||
|
|
||||||
tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), 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
|
/* Construct a playback::rvalue instance (wrapping a tree) for a
|
||||||
|
|
@ -912,12 +918,13 @@ playback::rvalue *
|
||||||
playback::context::
|
playback::context::
|
||||||
new_call_through_ptr (location *loc,
|
new_call_through_ptr (location *loc,
|
||||||
rvalue *fn_ptr,
|
rvalue *fn_ptr,
|
||||||
const auto_vec<rvalue *> *args)
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call)
|
||||||
{
|
{
|
||||||
gcc_assert (fn_ptr);
|
gcc_assert (fn_ptr);
|
||||||
tree t_fn_ptr = fn_ptr->as_tree ();
|
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. */
|
/* Construct a tree for a cast. */
|
||||||
|
|
|
||||||
|
|
@ -133,12 +133,14 @@ public:
|
||||||
rvalue *
|
rvalue *
|
||||||
new_call (location *loc,
|
new_call (location *loc,
|
||||||
function *func,
|
function *func,
|
||||||
const auto_vec<rvalue *> *args);
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call);
|
||||||
|
|
||||||
rvalue *
|
rvalue *
|
||||||
new_call_through_ptr (location *loc,
|
new_call_through_ptr (location *loc,
|
||||||
rvalue *fn_ptr,
|
rvalue *fn_ptr,
|
||||||
const auto_vec<rvalue *> *args);
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call);
|
||||||
|
|
||||||
rvalue *
|
rvalue *
|
||||||
new_cast (location *loc,
|
new_cast (location *loc,
|
||||||
|
|
@ -236,7 +238,8 @@ private:
|
||||||
rvalue *
|
rvalue *
|
||||||
build_call (location *loc,
|
build_call (location *loc,
|
||||||
tree fn_ptr,
|
tree fn_ptr,
|
||||||
const auto_vec<rvalue *> *args);
|
const auto_vec<rvalue *> *args,
|
||||||
|
bool require_tail_call);
|
||||||
|
|
||||||
tree
|
tree
|
||||||
build_cast (location *loc,
|
build_cast (location *loc,
|
||||||
|
|
|
||||||
|
|
@ -4681,6 +4681,39 @@ recording::cast::write_reproducer (reproducer &r)
|
||||||
r.get_identifier_as_type (get_type ()));
|
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 implementation of class gcc::jit::recording::call. */
|
||||||
|
|
||||||
/* The constructor for gcc::jit::recording::call. */
|
/* The constructor for gcc::jit::recording::call. */
|
||||||
|
|
@ -4690,12 +4723,9 @@ recording::call::call (recording::context *ctxt,
|
||||||
recording::function *func,
|
recording::function *func,
|
||||||
int numargs,
|
int numargs,
|
||||||
rvalue **args)
|
rvalue **args)
|
||||||
: rvalue (ctxt, loc, func->get_return_type ()),
|
: base_call (ctxt, loc, func->get_return_type (), numargs, args),
|
||||||
m_func (func),
|
m_func (func)
|
||||||
m_args ()
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i< numargs; i++)
|
|
||||||
m_args.safe_push (args[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of pure virtual hook recording::memento::replay_into
|
/* Implementation of pure virtual hook recording::memento::replay_into
|
||||||
|
|
@ -4711,7 +4741,8 @@ recording::call::replay_into (replayer *r)
|
||||||
|
|
||||||
set_playback_obj (r->new_call (playback_location (r, m_loc),
|
set_playback_obj (r->new_call (playback_location (r, m_loc),
|
||||||
m_func->playback_function (),
|
m_func->playback_function (),
|
||||||
&playback_args));
|
&playback_args,
|
||||||
|
m_require_tail_call));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of pure virtual hook recording::rvalue::visit_children
|
/* Implementation of pure virtual hook recording::rvalue::visit_children
|
||||||
|
|
@ -4790,6 +4821,7 @@ recording::call::write_reproducer (reproducer &r)
|
||||||
r.get_identifier (m_func),
|
r.get_identifier (m_func),
|
||||||
m_args.length (),
|
m_args.length (),
|
||||||
args_id);
|
args_id);
|
||||||
|
write_reproducer_tail_call (r, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The implementation of class gcc::jit::recording::call_through_ptr. */
|
/* The implementation of class gcc::jit::recording::call_through_ptr. */
|
||||||
|
|
@ -4801,14 +4833,12 @@ recording::call_through_ptr::call_through_ptr (recording::context *ctxt,
|
||||||
recording::rvalue *fn_ptr,
|
recording::rvalue *fn_ptr,
|
||||||
int numargs,
|
int numargs,
|
||||||
rvalue **args)
|
rvalue **args)
|
||||||
: rvalue (ctxt, loc,
|
: base_call (ctxt, loc,
|
||||||
fn_ptr->get_type ()->dereference ()
|
fn_ptr->get_type ()->dereference ()
|
||||||
->as_a_function_type ()->get_return_type ()),
|
->as_a_function_type ()->get_return_type (),
|
||||||
m_fn_ptr (fn_ptr),
|
numargs, args),
|
||||||
m_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
|
/* Implementation of pure virtual hook recording::memento::replay_into
|
||||||
|
|
@ -4824,7 +4854,8 @@ recording::call_through_ptr::replay_into (replayer *r)
|
||||||
|
|
||||||
set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
|
set_playback_obj (r->new_call_through_ptr (playback_location (r, m_loc),
|
||||||
m_fn_ptr->playback_rvalue (),
|
m_fn_ptr->playback_rvalue (),
|
||||||
&playback_args));
|
&playback_args,
|
||||||
|
m_require_tail_call));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of pure virtual hook recording::rvalue::visit_children
|
/* Implementation of pure virtual hook recording::rvalue::visit_children
|
||||||
|
|
@ -4907,6 +4938,7 @@ recording::call_through_ptr::write_reproducer (reproducer &r)
|
||||||
r.get_identifier_as_rvalue (m_fn_ptr),
|
r.get_identifier_as_rvalue (m_fn_ptr),
|
||||||
m_args.length (),
|
m_args.length (),
|
||||||
args_id);
|
args_id);
|
||||||
|
write_reproducer_tail_call (r, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The implementation of class gcc::jit::recording::array_access. */
|
/* The implementation of class gcc::jit::recording::array_access. */
|
||||||
|
|
|
||||||
|
|
@ -960,8 +960,9 @@ public:
|
||||||
void set_scope (function *scope);
|
void set_scope (function *scope);
|
||||||
function *get_scope () const { return m_scope; }
|
function *get_scope () const { return m_scope; }
|
||||||
|
|
||||||
/* Dynamic cast. */
|
/* Dynamic casts. */
|
||||||
virtual param *dyn_cast_param () { return NULL; }
|
virtual param *dyn_cast_param () { return NULL; }
|
||||||
|
virtual base_call *dyn_cast_base_call () { return NULL; }
|
||||||
|
|
||||||
virtual const char *access_as_rvalue (reproducer &r);
|
virtual const char *access_as_rvalue (reproducer &r);
|
||||||
|
|
||||||
|
|
@ -1418,7 +1419,36 @@ private:
|
||||||
rvalue *m_rvalue;
|
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:
|
public:
|
||||||
call (context *ctxt,
|
call (context *ctxt,
|
||||||
|
|
@ -1434,17 +1464,12 @@ public:
|
||||||
private:
|
private:
|
||||||
string * make_debug_string () FINAL OVERRIDE;
|
string * make_debug_string () FINAL OVERRIDE;
|
||||||
void write_reproducer (reproducer &r) FINAL OVERRIDE;
|
void write_reproducer (reproducer &r) FINAL OVERRIDE;
|
||||||
enum precedence get_precedence () const FINAL OVERRIDE
|
|
||||||
{
|
|
||||||
return PRECEDENCE_POSTFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
function *m_func;
|
function *m_func;
|
||||||
auto_vec<rvalue *> m_args;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class call_through_ptr : public rvalue
|
class call_through_ptr : public base_call
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
call_through_ptr (context *ctxt,
|
call_through_ptr (context *ctxt,
|
||||||
|
|
@ -1460,14 +1485,9 @@ public:
|
||||||
private:
|
private:
|
||||||
string * make_debug_string () FINAL OVERRIDE;
|
string * make_debug_string () FINAL OVERRIDE;
|
||||||
void write_reproducer (reproducer &r) FINAL OVERRIDE;
|
void write_reproducer (reproducer &r) FINAL OVERRIDE;
|
||||||
enum precedence get_precedence () const FINAL OVERRIDE
|
|
||||||
{
|
|
||||||
return PRECEDENCE_POSTFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rvalue *m_fn_ptr;
|
rvalue *m_fn_ptr;
|
||||||
auto_vec<rvalue *> m_args;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class array_access : public lvalue
|
class array_access : public lvalue
|
||||||
|
|
|
||||||
|
|
@ -2950,3 +2950,23 @@ gcc_jit_timer_print (gcc_jit_timer *timer,
|
||||||
timer->start (TV_TOTAL);
|
timer->start (TV_TOTAL);
|
||||||
timer->push (TV_JIT_CLIENT_CODE);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1374,6 +1374,19 @@ extern void
|
||||||
gcc_jit_timer_print (gcc_jit_timer *timer,
|
gcc_jit_timer_print (gcc_jit_timer *timer,
|
||||||
FILE *f_out);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
|
||||||
|
|
@ -145,3 +145,8 @@ LIBGCCJIT_ABI_5 {
|
||||||
global:
|
global:
|
||||||
gcc_jit_context_set_bool_use_external_driver;
|
gcc_jit_context_set_bool_use_external_driver;
|
||||||
} LIBGCCJIT_ABI_4;
|
} LIBGCCJIT_ABI_4;
|
||||||
|
|
||||||
|
LIBGCCJIT_ABI_6 {
|
||||||
|
global:
|
||||||
|
gcc_jit_rvalue_set_bool_require_tail_call;
|
||||||
|
} LIBGCCJIT_ABI_5;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
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>
|
2016-05-20 Jakub Jelinek <jakub@redhat.com>
|
||||||
|
|
||||||
PR fortran/71204
|
PR fortran/71204
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,13 @@
|
||||||
#undef create_code
|
#undef create_code
|
||||||
#undef verify_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 */
|
/* test-fibonacci.c */
|
||||||
#define create_code create_code_fibonacci
|
#define create_code create_code_fibonacci
|
||||||
#define verify_code verify_code_fibonacci
|
#define verify_code verify_code_fibonacci
|
||||||
|
|
@ -272,6 +279,9 @@ const struct testcase testcases[] = {
|
||||||
{"factorial",
|
{"factorial",
|
||||||
create_code_factorial,
|
create_code_factorial,
|
||||||
verify_code_factorial},
|
verify_code_factorial},
|
||||||
|
{"factorial_must_tail_call",
|
||||||
|
create_code_factorial_must_tail_call,
|
||||||
|
verify_code_factorial_must_tail_call},
|
||||||
{"fibonacci",
|
{"fibonacci",
|
||||||
create_code_fibonacci,
|
create_code_fibonacci,
|
||||||
verify_code_fibonacci},
|
verify_code_fibonacci},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue