diff options
author | dmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4> | 2016-05-20 19:12:49 +0000 |
---|---|---|
committer | dmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4> | 2016-05-20 19:12:49 +0000 |
commit | 9954d2306dfc918e8d3b2af592fedc2d943617c0 (patch) | |
tree | f210624d9cf8816cc56a0800f0429cd6e558a371 /gcc/jit | |
parent | 869bb2b63aa98a57409c46a2a42790742f50887d (diff) | |
download | gcc-9954d2306dfc918e8d3b2af592fedc2d943617c0.tar.gz |
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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@236531 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/jit')
-rw-r--r-- | gcc/jit/ChangeLog | 44 | ||||
-rw-r--r-- | gcc/jit/docs/topics/compatibility.rst | 7 | ||||
-rw-r--r-- | gcc/jit/docs/topics/expressions.rst | 24 | ||||
-rw-r--r-- | gcc/jit/jit-common.h | 1 | ||||
-rw-r--r-- | gcc/jit/jit-playback.c | 23 | ||||
-rw-r--r-- | gcc/jit/jit-playback.h | 9 | ||||
-rw-r--r-- | gcc/jit/jit-recording.c | 60 | ||||
-rw-r--r-- | gcc/jit/jit-recording.h | 46 | ||||
-rw-r--r-- | gcc/jit/libgccjit.c | 20 | ||||
-rw-r--r-- | gcc/jit/libgccjit.h | 13 | ||||
-rw-r--r-- | gcc/jit/libgccjit.map | 5 |
11 files changed, 214 insertions, 38 deletions
diff --git a/gcc/jit/ChangeLog b/gcc/jit/ChangeLog index f9320eccee0..1c3e1fc8f21 100644 --- a/gcc/jit/ChangeLog +++ b/gcc/jit/ChangeLog @@ -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> * dummy-frontend.c: Include diagnostic.h. diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index d9eacf27c6d..7abd0508e20 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -135,3 +135,10 @@ entrypoints: ------------------- ``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` diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst index 04453321b9c..261483c78b0 100644 --- a/gcc/jit/docs/topics/expressions.rst +++ b/gcc/jit/docs/topics/expressions.rst @@ -424,6 +424,30 @@ Function calls 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 ************* diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index 8a6cd7453a3..b48ea0db69d 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -126,6 +126,7 @@ namespace recording { class local; class global; class param; + class base_call; class statement; class case_; diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c index 156448d13d5..c9f40848be0 100644 --- a/gcc/jit/jit-playback.c +++ b/gcc/jit/jit-playback.c @@ -854,7 +854,8 @@ playback::rvalue * 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 ()); @@ -868,9 +869,13 @@ build_call (location *loc, 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 @@ -890,7 +895,8 @@ playback::rvalue * playback::context:: new_call (location *loc, function *func, - const auto_vec<rvalue *> *args) + const auto_vec<rvalue *> *args, + bool require_tail_call) { tree fndecl; @@ -902,7 +908,7 @@ new_call (location *loc, 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 @@ -912,12 +918,13 @@ playback::rvalue * 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. */ diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index 8f7a43db1b0..c00c25820af 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -133,12 +133,14 @@ public: 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, @@ -236,7 +238,8 @@ private: 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, diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 8f5f914277c..937634207d6 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -4681,6 +4681,39 @@ recording::cast::write_reproducer (reproducer &r) 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. */ @@ -4690,12 +4723,9 @@ recording::call::call (recording::context *ctxt, 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 @@ -4711,7 +4741,8 @@ recording::call::replay_into (replayer *r) 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 @@ -4790,6 +4821,7 @@ recording::call::write_reproducer (reproducer &r) 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. */ @@ -4801,14 +4833,12 @@ recording::call_through_ptr::call_through_ptr (recording::context *ctxt, 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 @@ -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), m_fn_ptr->playback_rvalue (), - &playback_args)); + &playback_args, + m_require_tail_call)); } /* 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), m_args.length (), args_id); + write_reproducer_tail_call (r, id); } /* The implementation of class gcc::jit::recording::array_access. */ diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 1c3e7634209..0e3511c6a1c 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -960,8 +960,9 @@ public: 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); @@ -1418,7 +1419,36 @@ private: 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, @@ -1434,17 +1464,12 @@ public: 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, @@ -1460,14 +1485,9 @@ public: 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 diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index 02ff50c75a8..c2c6aeb5bfb 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -2950,3 +2950,23 @@ gcc_jit_timer_print (gcc_jit_timer *timer, 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); +} diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index a8b9f55dc1b..175cc16c01d 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -1374,6 +1374,19 @@ extern void 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 */ diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index 65f3166f694..545b192b64c 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -145,3 +145,8 @@ LIBGCCJIT_ABI_5 { 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; |