aboutsummaryrefslogtreecommitdiff
path: root/gcc/jit/libgccjit.c
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2015-06-30 20:39:50 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2015-06-30 20:39:50 +0000
commitec5d0088148d1d6218f5f503d1c8e67a529dfacd (patch)
treec7cf3f33890bd8579a8afa8a850a326bc0b1fd58 /gcc/jit/libgccjit.c
parent6a3603e356e14096e9554ceef57916fd03d9072b (diff)
downloadgcc-ec5d0088148d1d6218f5f503d1c8e67a529dfacd.zip
gcc-ec5d0088148d1d6218f5f503d1c8e67a529dfacd.tar.gz
gcc-ec5d0088148d1d6218f5f503d1c8e67a529dfacd.tar.bz2
jit: add switch statements
gcc/ChangeLog: * typed-splay-tree.h: New file. gcc/jit/ChangeLog: * docs/cp/topics/functions.rst (Blocks): Add switch statements to list of ways to terminate a block. (gccjit::block::end_with_switch): Add function description. (gccjit::case_): Add class. (gccjit::context::new_case): Add function description. * docs/cp/topics/objects.rst: Add "case_" to class hierarchy. * docs/topics/compatibility.rst (LIBGCCJIT_ABI_3): New. * docs/topics/functions.rst (Blocks): Add switch statements to list of ways to terminate a block. (gcc_jit_block_end_with_switch): Add function description. (gcc_jit_case): Add type. (gcc_jit_context_new_case): Add function description. (gcc_jit_case_as_object): Add function description. * docs/topics/objects.rst: Add gcc_jit_case to class hierarchy. * docs/_build/texinfo/libgccjit.texi: Regenerate. * jit-common.h (gcc::jit::recording::case_): Add forward decl. (gcc::jit::playback::case_): Add forward decl. * jit-playback.c (add_case): New function. (gcc::jit::playback::block::add_switch): New function. * jit-playback.h (gcc::jit::playback::case_): New struct. (gcc::jit::playback::block::get_function): New method. (gcc::jit::playback::block::add_switch): New method. * jit-recording.c: Within namespace gcc::jit... (recording::context::new_case): New method. (recording::function::validate): Update for change to get_successor_blocks. (recording::block::end_with_switch): New method. (recording::block::get_successor_blocks): Update to support an arbitrary number of successor blocks. (recording::block::dump_edges_to_dot): Likewise. (memento_of_new_rvalue_from_const <int>::get_wide_int): New. (memento_of_new_rvalue_from_const <long>::get_wide_int): New. (memento_of_new_rvalue_from_const <double>::get_wide_int): New. (memento_of_new_rvalue_from_const <void *>::get_wide_int): New. (recording::statement::get_successor_blocks): Update to support an arbitrary number of successor blocks. (recording::conditional::get_successor_blocks): Likewise. (recording::jump::get_successor_blocks): Likewise. (recording::return_::get_successor_blocks): Likewise. (recording::case_::write_reproducer): New. (recording::case_::make_debug_string): New. (recording::switch_::switch_): New. (recording::switch_::replay_into): New. (recording::switch_::get_successor_blocks): New. (recording::switch_::make_debug_string): New. (recording::switch_::write_reproducer): New. * jit-recording.h: Within namespace gcc::jit::recording... (context::new_case): New. (rvalue::is_constant): New. (rvalue::get_wide_int): New. (block::end_with_switch): New. (block::get_successor_blocks): Update to support an arbitrary number of successor blocks. (memento_of_new_rvalue_from_const::is_constant): New. (memento_of_new_rvalue_from_const::get_wide_int): New. (statement::get_successor_blocks): Update to support an arbitrary number of successor blocks. (conditional::get_successor_blocks): Likewise. (jump::get_successor_blocks): Likewise. (return_::get_successor_blocks): Likewise. (case_): New subclass of memento. (switch_): New subclass of statement. * libgccjit++.h (gccjit::case_): New subclass of gccjit::object. (gccjit::context::new_case): New method. (gccjit::block::end_with_switch): New method. (gccjit::case_::case): New ctors. (gccjit::case_::get_inner_case): New method. * libgccjit.c: Include "typed-splay-tree.h" (struct gcc_jit_case): New. (gcc_jit_context_new_case): New function. (gcc_jit_case_as_object): New function. (valid_dest_for_switch): New function. (valid_case_for_switch): New function. (class api_call_validator): New class. (class case_range_validator): New class. (case_range_validator::case_range_validator): New. (case_range_validator::validate): New. (case_range_validator::case_compare): New. (case_range_validator::get_wide_int): new. (gcc_jit_block_end_with_switch): New. * libgccjit.h: Add gcc_jit_case to class hierarchy comment. (gcc_jit_case): New typedef. (gcc_jit_context_new_case): New function. (gcc_jit_case_as_object): New function. (gcc_jit_block_end_with_switch): New function. (LIBGCCJIT_HAVE_SWITCH_STATEMENTS): New. * libgccjit.map: Add gcc_jit_block_end_with_switch, gcc_jit_case_as_object and gcc_jit_context_new_case. gcc/testsuite/ChangeLog: * jit.dg/all-non-failing-tests.h: Add test-switch.c. * jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c: New testcase. * jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c: New testcase. * jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c: New testcase. * jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c: New testcase. * jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c: New testcase. * jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c: New testcase. * jit.dg/test-switch.c: New testcase. * jit.dg/test-switch.cc: New testcase. From-SVN: r225207
Diffstat (limited to 'gcc/jit/libgccjit.c')
-rw-r--r--gcc/jit/libgccjit.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 7e0bfa6..4d7dd8c 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "opts.h"
#include "safe-ctype.h"
+#include "typed-splay-tree.h"
#include "libgccjit.h"
#include "jit-common.h"
@@ -84,6 +85,10 @@ struct gcc_jit_param : public gcc::jit::recording::param
{
};
+struct gcc_jit_case : public gcc::jit::recording::case_
+{
+};
+
/**********************************************************************
Error-handling.
@@ -2123,6 +2128,382 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block,
block->end_with_return (loc, NULL);
}
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
+ gcc::jit::recording::context::new_case method in
+ jit-recording.c. */
+
+gcc_jit_case *
+gcc_jit_context_new_case (gcc_jit_context *ctxt,
+ gcc_jit_rvalue *min_value,
+ gcc_jit_rvalue *max_value,
+ gcc_jit_block *block)
+{
+ RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+ JIT_LOG_FUNC (ctxt->get_logger ());
+ RETURN_NULL_IF_FAIL (min_value, ctxt, NULL, "NULL min_value");
+ RETURN_NULL_IF_FAIL (max_value, ctxt, NULL, "NULL max_value");
+ RETURN_NULL_IF_FAIL (block, ctxt, NULL, "NULL block");
+
+ RETURN_NULL_IF_FAIL_PRINTF1 (min_value->is_constant (), ctxt, NULL,
+ "min_value is not a constant: %s",
+ min_value->get_debug_string ());
+ RETURN_NULL_IF_FAIL_PRINTF1 (max_value->is_constant (), ctxt, NULL,
+ "max_value is not a constant: %s",
+ max_value->get_debug_string ());
+ RETURN_NULL_IF_FAIL_PRINTF2 (
+ min_value->get_type ()->is_int (),
+ ctxt, NULL,
+ "min_value: %s (type: %s) is not of integer type",
+ min_value->get_debug_string (),
+ min_value->get_type ()->get_debug_string ());
+ RETURN_NULL_IF_FAIL_PRINTF2 (
+ max_value->get_type ()->is_int (),
+ ctxt, NULL,
+ "max_value: %s (type: %s) is not of integer type",
+ max_value->get_debug_string (),
+ max_value->get_type ()->get_debug_string ());
+
+ wide_int wi_min, wi_max;
+ if (!min_value->get_wide_int (&wi_min))
+ gcc_unreachable ();
+ if (!max_value->get_wide_int (&wi_max))
+ gcc_unreachable ();
+ RETURN_NULL_IF_FAIL_PRINTF2 (
+ wi::les_p (wi_min, wi_max),
+ ctxt, NULL,
+ "min_value: %s > max_value: %s",
+ min_value->get_debug_string (),
+ max_value->get_debug_string ());
+ return (gcc_jit_case *)ctxt->new_case (min_value,
+ max_value,
+ block);
+}
+
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, this calls the trivial
+ gcc::jit::recording::memento::as_object method (a case is a
+ memento), in jit-recording.h. */
+
+gcc_jit_object *
+gcc_jit_case_as_object (gcc_jit_case *case_)
+{
+ RETURN_NULL_IF_FAIL (case_, NULL, NULL, "NULL case");
+
+ return static_cast <gcc_jit_object *> (case_->as_object ());
+}
+
+/* Helper function for gcc_jit_block_end_with_switch and
+ valid_case_for_switch. */
+
+static bool
+valid_dest_for_switch (gcc::jit::recording::context *ctxt,
+ gcc_jit_location *loc,
+ const char *api_funcname,
+ gcc::jit::recording::block *switch_block,
+ gcc::jit::recording::block *dest_block,
+ const char *dest_block_desc)
+{
+ if (!dest_block)
+ {
+ jit_error (ctxt, loc, "%s: NULL %s", api_funcname, dest_block_desc);
+ return false;
+ }
+ gcc::jit::recording::function *switch_fn = switch_block->get_function ();
+ gcc::jit::recording::function *dest_fn = dest_block->get_function ();
+ if (switch_fn != dest_fn)
+ {
+ jit_error (ctxt, loc,
+ "%s: %s is not in same function:"
+ " switch block %s is in function %s"
+ " whereas %s %s is in function %s",
+ api_funcname,
+ dest_block_desc,
+ switch_block->get_debug_string (),
+ switch_fn->get_debug_string (),
+ dest_block_desc,
+ dest_block->get_debug_string (),
+ dest_fn->get_debug_string ());
+ return false;
+ }
+ return true;
+}
+
+/* Helper function for gcc_jit_block_end_with_switch. */
+
+static bool
+valid_case_for_switch (gcc::jit::recording::context *ctxt,
+ gcc_jit_location *loc,
+ const char *api_funcname,
+ gcc_jit_block *switch_block,
+ gcc_jit_rvalue *expr,
+ gcc_jit_case *case_,
+ const char *case_desc,
+ int case_idx)
+{
+ if (!case_)
+ {
+ jit_error (ctxt, loc,
+ "%s:"
+ " NULL case %i",
+ api_funcname,
+ case_idx);
+ return false;
+ }
+ if (!valid_dest_for_switch (ctxt, loc,
+ api_funcname,
+ switch_block,
+ case_->get_dest_block (),
+ case_desc))
+ return false;
+ gcc::jit::recording::type *expr_type = expr->get_type ();
+ if (expr_type != case_->get_min_value ()->get_type ())
+ {
+ jit_error (ctxt, loc,
+ "%s:"
+ " mismatching types between case and expression:"
+ " cases[%i]->min_value: %s (type: %s)"
+ " expr: %s (type: %s)",
+ api_funcname,
+ case_idx,
+ case_->get_min_value ()->get_debug_string (),
+ case_->get_min_value ()->get_type ()->get_debug_string (),
+ expr->get_debug_string (),
+ expr_type->get_debug_string ());
+ return false;
+ }
+ if (expr_type != case_->get_max_value ()->get_type ())
+ {
+ jit_error (ctxt, loc,
+ "%s:"
+ " mismatching types between case and expression:"
+ " cases[%i]->max_value: %s (type: %s)"
+ " expr: %s (type: %s)",
+ api_funcname,
+ case_idx,
+ case_->get_max_value ()->get_debug_string (),
+ case_->get_max_value ()->get_type ()->get_debug_string (),
+ expr->get_debug_string (),
+ expr_type->get_debug_string ());
+ return false;
+ }
+ return true;
+}
+
+/* A class for holding the data we need to perform error-checking
+ on a libgccjit API call. */
+
+class api_call_validator
+{
+ public:
+ api_call_validator (gcc::jit::recording::context *ctxt,
+ gcc_jit_location *loc,
+ const char *funcname)
+ : m_ctxt (ctxt),
+ m_loc (loc),
+ m_funcname (funcname)
+ {}
+
+ protected:
+ gcc::jit::recording::context *m_ctxt;
+ gcc_jit_location *m_loc;
+ const char *m_funcname;
+};
+
+/* A class for verifying that the ranges of cases within
+ gcc_jit_block_end_with_switch don't overlap. */
+
+class case_range_validator : public api_call_validator
+{
+ public:
+ case_range_validator (gcc::jit::recording::context *ctxt,
+ gcc_jit_location *loc,
+ const char *funcname);
+
+ bool
+ validate (gcc_jit_case *case_, int idx);
+
+ private:
+ static int
+ case_compare (gcc::jit::recording::rvalue *k1,
+ gcc::jit::recording::rvalue *k2);
+
+ static wide_int
+ get_wide_int (gcc::jit::recording::rvalue *k);
+
+ private:
+ typed_splay_tree <gcc::jit::recording::rvalue *, gcc_jit_case *> m_cases;
+};
+
+/* case_range_validator's ctor. */
+
+case_range_validator::case_range_validator (gcc::jit::recording::context *ctxt,
+ gcc_jit_location *loc,
+ const char *funcname)
+: api_call_validator (ctxt, loc, funcname),
+ m_cases (case_compare, NULL, NULL)
+{
+}
+
+/* Ensure that the range of CASE_ does not overlap with any of the
+ ranges of cases we've already seen.
+ Return true if everything is OK.
+ Return false and emit an error if there is an overlap.
+ Compare with c-family/c-common.c:c_add_case_label. */
+
+bool
+case_range_validator::validate (gcc_jit_case *case_,
+ int case_idx)
+{
+ /* Look up the LOW_VALUE in the table of case labels we already
+ have. */
+ gcc_jit_case *other = m_cases.lookup (case_->get_min_value ());
+
+ /* If there was not an exact match, check for overlapping ranges. */
+ if (!other)
+ {
+ gcc_jit_case *pred;
+ gcc_jit_case *succ;
+
+ /* Even though there wasn't an exact match, there might be an
+ overlap between this case range and another case range.
+ Since we've (inductively) not allowed any overlapping case
+ ranges, we simply need to find the greatest low case label
+ that is smaller that CASE_MIN_VALUE, and the smallest low case
+ label that is greater than CASE_MAX_VALUE. If there is an overlap
+ it will occur in one of these two ranges. */
+ pred = m_cases.predecessor (case_->get_min_value ());
+ succ = m_cases.successor (case_->get_max_value ());
+
+ /* Check to see if the PRED overlaps. It is smaller than
+ the LOW_VALUE, so we only need to check its max value. */
+ if (pred)
+ {
+ wide_int wi_case_min = get_wide_int (case_->get_min_value ());
+ wide_int wi_pred_max = get_wide_int (pred->get_max_value ());
+ if (wi::ges_p (wi_pred_max, wi_case_min))
+ other = pred;
+ }
+
+ if (!other && succ)
+ {
+ /* Check to see if the SUCC overlaps. The low end of that
+ range is bigger than the low end of the current range. */
+ wide_int wi_case_max = get_wide_int (case_->get_max_value ());
+ wide_int wi_succ_min = get_wide_int (succ->get_min_value ());
+ if (wi::les_p (wi_succ_min, wi_case_max))
+ other = succ;
+ }
+ }
+
+ /* If there was an overlap, issue an error. */
+ if (other)
+ {
+ jit_error (m_ctxt, m_loc,
+ "%s: duplicate (or overlapping) cases values:"
+ " case %i: %s overlaps %s",
+ m_funcname,
+ case_idx,
+ case_->get_debug_string (),
+ other->get_debug_string ());
+ return false;
+ }
+
+ /* Register this case label in the splay tree. */
+ m_cases.insert (case_->get_min_value (),
+ case_);
+ return true;
+}
+
+/* Compare with c-family/c-common.c:case_compare, which acts on tree
+ nodes, rather than rvalue *.
+
+ Comparator for case label values. K1 and K2 must be constant integer
+ values (anything else should have been rejected by
+ gcc_jit_context_new_case.
+
+ Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after
+ K2, and 0 if K1 and K2 are equal. */
+
+int
+case_range_validator::case_compare (gcc::jit::recording::rvalue * k1,
+ gcc::jit::recording::rvalue * k2)
+{
+ wide_int wi1 = get_wide_int (k1);
+ wide_int wi2 = get_wide_int (k2);
+ return wi::cmps(wi1, wi2);
+}
+
+/* Given a const int rvalue K, get the underlying value as a wide_int. */
+
+wide_int
+case_range_validator::get_wide_int (gcc::jit::recording::rvalue *k)
+{
+ wide_int wi;
+ bool got_wi = k->get_wide_int (&wi);
+ gcc_assert (got_wi);
+ return wi;
+}
+
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
+ gcc::jit::recording::block::end_with_switch method in
+ jit-recording.c. */
+
+void
+gcc_jit_block_end_with_switch (gcc_jit_block *block,
+ gcc_jit_location *loc,
+ gcc_jit_rvalue *expr,
+ gcc_jit_block *default_block,
+ int num_cases,
+ gcc_jit_case **cases)
+{
+ RETURN_IF_NOT_VALID_BLOCK (block, loc);
+ gcc::jit::recording::context *ctxt = block->get_context ();
+ JIT_LOG_FUNC (ctxt->get_logger ());
+ /* LOC can be NULL. */
+ RETURN_IF_FAIL (expr, ctxt, loc,
+ "NULL expr");
+ gcc::jit::recording::type *expr_type = expr->get_type ();
+ RETURN_IF_FAIL_PRINTF2 (
+ expr_type->is_int (),
+ ctxt, loc,
+ "expr: %s (type: %s) is not of integer type",
+ expr->get_debug_string (),
+ expr_type->get_debug_string ());
+ if (!valid_dest_for_switch (ctxt, loc,
+ __func__,
+ block,
+ default_block,
+ "default_block"))
+ return;
+ RETURN_IF_FAIL (num_cases >= 0, ctxt, loc, "num_cases < 0");
+ case_range_validator crv (ctxt, loc, __func__);
+ for (int i = 0; i < num_cases; i++)
+ {
+ char case_desc[32];
+ snprintf (case_desc, sizeof (case_desc),
+ "cases[%i]", i);
+ if (!valid_case_for_switch (ctxt, loc,
+ __func__,
+ block,
+ expr,
+ cases[i],
+ case_desc,
+ i))
+ return;
+ if (!crv.validate (cases[i], i))
+ return;
+ }
+
+ block->end_with_switch (loc, expr, default_block,
+ num_cases,
+ (gcc::jit::recording::case_ **)cases);
+}
+
/**********************************************************************
Option-management
**********************************************************************/