Commit ec5d0088 by David Malcolm Committed by David Malcolm

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
parent 6a3603e3
2015-06-30 David Malcolm <dmalcolm@redhat.com>
* typed-splay-tree.h: New file.
2015-06-30 Vladimir Makarov <vmakarov@redhat.com>
PR debug/66691
......
2015-06-30 David Malcolm <dmalcolm@redhat.com>
* 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.
2015-06-30 David Malcolm <dmalcolm@redhat.com>
PR jit/66546
* docs/cp/topics/contexts.rst
(gccjit::context::set_bool_allow_unreachable_blocks): New.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -98,7 +98,8 @@ Blocks
be the entrypoint.
Each basic block that you create within a function must be
terminated, either with a conditional, a jump, or a return.
terminated, either with a conditional, a jump, a return, or
a switch.
It's legal to have multiple basic blocks that return within
one function.
......@@ -241,3 +242,82 @@ Statements
.. code-block:: c
return;
.. function:: void\
gccjit::block::end_with_switch (gccjit::rvalue expr,\
gccjit::block default_block,\
std::vector <gccjit::case_> cases,\
gccjit::location loc)
Terminate a block by adding evalation of an rvalue, then performing
a multiway branch.
This is roughly equivalent to this C code:
.. code-block:: c
switch (expr)
{
default:
goto default_block;
case C0.min_value ... C0.max_value:
goto C0.dest_block;
case C1.min_value ... C1.max_value:
goto C1.dest_block;
...etc...
case C[N - 1].min_value ... C[N - 1].max_value:
goto C[N - 1].dest_block;
}
``expr`` must be of the same integer type as all of the ``min_value``
and ``max_value`` within the cases.
The ranges of the cases must not overlap (or have duplicate
values).
The API entrypoints relating to switch statements and cases:
* :func:`gccjit::block::end_with_switch`
* :func:`gccjit::context::new_case`
were added in :ref:`LIBGCCJIT_ABI_3`; you can test for their presence
using
.. code-block:: c
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
.. class:: gccjit::case_
A `gccjit::case_` represents a case within a switch statement, and
is created within a particular :class:`gccjit::context` using
:func:`gccjit::context::new_case`. It is a subclass of
:class:`gccjit::object`.
Each case expresses a multivalued range of integer values. You
can express single-valued cases by passing in the same value for
both `min_value` and `max_value`.
.. function:: gccjit::case_ *\
gccjit::context::new_case (gccjit::rvalue min_value,\
gccjit::rvalue max_value,\
gccjit::block dest_block)
Create a new gccjit::case for use in a switch statement.
`min_value` and `max_value` must be constants of an integer type,
which must match that of the expression of the switch statement.
`dest_block` must be within the same function as the switch
statement.
Here's an example of creating a switch statement:
.. literalinclude:: ../../../../testsuite/jit.dg/test-switch.cc
:start-after: /* Quote from here in docs/cp/topics/functions.rst. */
:end-before: /* Quote up to here in docs/cp/topics/functions.rst. */
:language: c++
......@@ -46,6 +46,7 @@ The C++ class hierarchy within the ``gccjit`` namespace looks like this::
+- rvalue
+- lvalue
+- param
+- case_
The :class:`gccjit::object` base class has the following operations:
......
......@@ -47,7 +47,6 @@ from metadata by using ``objdump``:
0x00824161 0x00 04 LIBGCCJIT_ABI_1
0x00824160 0x00 03 LIBGCCJIT_ABI_0
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
You can see the symbol tags provided by libgccjit.so using ``objdump``:
......@@ -95,3 +94,16 @@ continue to work, with this being handled transparently by the linker
-------------------
``LIBGCCJIT_ABI_2`` covers the addition of
:func:`gcc_jit_context_set_bool_allow_unreachable_blocks`
.. _LIBGCCJIT_ABI_3:
``LIBGCCJIT_ABI_3``
-------------------
``LIBGCCJIT_ABI_3`` covers the addition of switch statements via API
entrypoints:
* :func:`gcc_jit_block_end_with_switch`
* :func:`gcc_jit_case_as_object`
* :func:`gcc_jit_context_new_case`
......@@ -153,7 +153,8 @@ Blocks
be the entrypoint.
Each basic block that you create within a function must be
terminated, either with a conditional, a jump, or a return.
terminated, either with a conditional, a jump, a return, or a
switch.
It's legal to have multiple basic blocks that return within
one function.
......@@ -313,3 +314,96 @@ Statements
.. code-block:: c
return;
.. function:: 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)
Terminate a block by adding evalation of an rvalue, then performing
a multiway branch.
This is roughly equivalent to this C code:
.. code-block:: c
switch (expr)
{
default:
goto default_block;
case C0.min_value ... C0.max_value:
goto C0.dest_block;
case C1.min_value ... C1.max_value:
goto C1.dest_block;
...etc...
case C[N - 1].min_value ... C[N - 1].max_value:
goto C[N - 1].dest_block;
}
``block``, ``expr``, ``default_block`` and ``cases`` must all be
non-NULL.
``expr`` must be of the same integer type as all of the ``min_value``
and ``max_value`` within the cases.
``num_cases`` must be >= 0.
The ranges of the cases must not overlap (or have duplicate
values).
The API entrypoints relating to switch statements and cases:
* :c:func:`gcc_jit_block_end_with_switch`
* :c:func:`gcc_jit_case_as_object`
* :c:func:`gcc_jit_context_new_case`
were added in :ref:`LIBGCCJIT_ABI_3`; you can test for their presence
using
.. code-block:: c
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
.. type:: gcc_jit_case
A `gcc_jit_case` represents a case within a switch statement, and
is created within a particular :c:type:`gcc_jit_context` using
:c:func:`gcc_jit_context_new_case`.
Each case expresses a multivalued range of integer values. You
can express single-valued cases by passing in the same value for
both `min_value` and `max_value`.
.. function:: 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 *dest_block)
Create a new gcc_jit_case instance for use in a switch statement.
`min_value` and `max_value` must be constants of an integer type,
which must match that of the expression of the switch statement.
`dest_block` must be within the same function as the switch
statement.
.. function:: gcc_jit_object *\
gcc_jit_case_as_object (gcc_jit_case *case_)
Upcast from a case to an object.
Here's an example of creating a switch statement:
.. literalinclude:: ../../../testsuite/jit.dg/test-switch.c
:start-after: /* Quote from here in docs/topics/functions.rst. */
:end-before: /* Quote up to here in docs/topics/functions.rst. */
:language: c
......@@ -47,6 +47,7 @@ looks like this::
+- gcc_jit_rvalue
+- gcc_jit_lvalue
+- gcc_jit_param
+- gcc_jit_case
There are casting methods for upcasting from subclasses to parent classes.
For example, :c:func:`gcc_jit_type_as_object`:
......
......@@ -129,6 +129,7 @@ namespace recording {
class global;
class param;
class statement;
class case_;
/* End of recording types. */
}
......@@ -150,6 +151,7 @@ namespace playback {
class source_file;
class source_line;
class location;
class case_;
/* End of playback types. */
}
......
......@@ -1576,6 +1576,80 @@ add_return (location *loc,
add_stmt (return_stmt);
}
/* Helper function for playback::block::add_switch.
Construct a case label for the given range, followed by a goto stmt
to the given block, appending them to stmt list *ptr_t_switch_body. */
static void
add_case (tree *ptr_t_switch_body,
tree t_low_value,
tree t_high_value,
playback::block *dest_block)
{
tree t_label = create_artificial_label (UNKNOWN_LOCATION);
DECL_CONTEXT (t_label) = dest_block->get_function ()->as_fndecl ();
tree t_case_label =
build_case_label (t_low_value, t_high_value, t_label);
append_to_statement_list (t_case_label, ptr_t_switch_body);
tree t_goto_stmt =
build1 (GOTO_EXPR, void_type_node, dest_block->as_label_decl ());
append_to_statement_list (t_goto_stmt, ptr_t_switch_body);
}
/* Add a switch statement to the function's statement list.
My initial attempt at implementing this constructed a TREE_VEC
of the cases and set it as SWITCH_LABELS (switch_expr). However,
gimplify.c:gimplify_switch_expr is set up to deal with SWITCH_BODY, and
doesn't have any logic for gimplifying SWITCH_LABELS.
Hence we create a switch body, and populate it with case labels, each
followed by a goto to the desired block. */
void
playback::block::
add_switch (location *loc,
rvalue *expr,
block *default_block,
const auto_vec <case_> *cases)
{
/* Compare with:
- c/c-typeck.c: c_start_case
- c-family/c-common.c:c_add_case_label
- java/expr.c:expand_java_switch and expand_java_add_case
We've already rejected overlaps and duplicates in
libgccjit.c:case_range_validator::validate. */
tree t_expr = expr->as_tree ();
tree t_type = TREE_TYPE (t_expr);
tree t_switch_body = alloc_stmt_list ();
int i;
case_ *c;
FOR_EACH_VEC_ELT (*cases, i, c)
{
tree t_low_value = c->m_min_value->as_tree ();
tree t_high_value = c->m_max_value->as_tree ();
add_case (&t_switch_body,
t_low_value,
t_high_value,
c->m_dest_block);
}
/* Default label. */
add_case (&t_switch_body,
NULL_TREE, NULL_TREE,
default_block);
tree switch_stmt = build3 (SWITCH_EXPR, t_type, t_expr,
t_switch_body, NULL_TREE);
if (loc)
set_tree_location (switch_stmt, loc);
add_stmt (switch_stmt);
}
/* Constructor for gcc::jit::playback::block. */
playback::block::
......
......@@ -437,6 +437,19 @@ private:
vec<block *> m_blocks;
};
struct case_
{
case_ (rvalue *min_value, rvalue *max_value, block *dest_block)
: m_min_value (min_value),
m_max_value (max_value),
m_dest_block (dest_block)
{}
rvalue *m_min_value;
rvalue *m_max_value;
block *m_dest_block;
};
class block : public wrapper
{
public:
......@@ -447,6 +460,8 @@ public:
tree as_label_decl () const { return m_label_decl; }
function *get_function () const { return m_func; }
void
add_eval (location *loc,
rvalue *rvalue);
......@@ -478,6 +493,12 @@ public:
add_return (location *loc,
rvalue *rvalue);
void
add_switch (location *loc,
rvalue *expr,
block *default_block,
const auto_vec <case_> *cases);
private:
void
set_tree_location (tree t, location *loc)
......
......@@ -1084,6 +1084,22 @@ recording::context::new_array_access (recording::location *loc,
return result;
}
/* Create a recording::case_ instance and add it to this context's list
of mementos.
Implements the post-error-checking part of
gcc_jit_context_new_case. */
recording::case_ *
recording::context::new_case (recording::rvalue *min_value,
recording::rvalue *max_value,
recording::block *block)
{
recording::case_ *result = new case_ (this, min_value, max_value, block);
record (result);
return result;
}
/* Set the given string option for this context, or add an error if
it's not recognized.
......@@ -3505,23 +3521,13 @@ recording::function::validate ()
/* Add successor blocks that aren't yet marked to the worklist. */
/* We checked that each block has a terminating statement above . */
block *next1, *next2;
int n = b->get_successor_blocks (&next1, &next2);
switch (n)
{
default:
gcc_unreachable ();
case 2:
if (!next2->m_is_reachable)
worklist.safe_push (next2);
/* fallthrough */
case 1:
if (!next1->m_is_reachable)
worklist.safe_push (next1);
break;
case 0:
break;
}
vec <block *> successors = b->get_successor_blocks ();
int i;
block *succ;
FOR_EACH_VEC_ELT (successors, i, succ)
if (!succ->m_is_reachable)
worklist.safe_push (succ);
successors.release ();
}
/* Now complain about any blocks that haven't been marked. */
......@@ -3769,6 +3775,30 @@ recording::block::end_with_return (recording::location *loc,
return result;
}
/* Create a recording::switch_ instance and add it to
the block's context's list of mementos, and to the block's
list of statements.
Implements the heart of gcc_jit_block_end_with_switch. */
recording::statement *
recording::block::end_with_switch (recording::location *loc,
recording::rvalue *expr,
recording::block *default_block,
int num_cases,
recording::case_ **cases)
{
statement *result = new switch_ (this, loc,
expr,
default_block,
num_cases,
cases);
m_ctxt->record (result);
m_statements.safe_push (result);
m_has_been_terminated = true;
return result;
}
/* Override the default implementation of
recording::memento::write_to_dump for blocks by writing
an unindented block name as a label, followed by the indented
......@@ -3846,24 +3876,20 @@ recording::block::get_last_statement () const
return NULL;
}
/* Assuming that this block has been terminated, get the number of
successor blocks, which will be 0, 1 or 2, for return, unconditional
jump, and conditional jump respectively.
NEXT1 and NEXT2 must be non-NULL. The first successor block (if any)
is written to NEXT1, and the second (if any) to NEXT2.
/* Assuming that this block has been terminated, get the successor blocks
as a vector. Ownership of the vector transfers to the caller, which
must call its release () method.
Used when validating functions, and when dumping dot representations
of them. */
int
recording::block::get_successor_blocks (block **next1, block **next2) const
vec <recording::block *>
recording::block::get_successor_blocks () const
{
gcc_assert (m_has_been_terminated);
gcc_assert (next1);
gcc_assert (next2);
statement *last_statement = get_last_statement ();
gcc_assert (last_statement);
return last_statement->get_successor_blocks (next1, next2);
return last_statement->get_successor_blocks ();
}
/* Implementation of pure virtual hook recording::memento::replay_into
......@@ -3941,12 +3967,14 @@ recording::block::dump_to_dot (pretty_printer *pp)
void
recording::block::dump_edges_to_dot (pretty_printer *pp)
{
block *next[2];
int num_succs = get_successor_blocks (&next[0], &next[1]);
for (int i = 0; i < num_succs; i++)
vec <block *> successors = get_successor_blocks ();
int i;
block *succ;
FOR_EACH_VEC_ELT (successors, i, succ)
pp_printf (pp,
"\tblock_%d:s -> block_%d:n;\n",
m_index, next[i]->m_index);
m_index, succ->m_index);
successors.release ();
}
/* The implementation of class gcc::jit::recording::global. */
......@@ -4091,6 +4119,16 @@ memento_of_new_rvalue_from_const <int>::make_debug_string ()
m_value);
}
/* The get_wide_int specialization for <int>. */
template <>
bool
memento_of_new_rvalue_from_const <int>::get_wide_int (wide_int *out) const
{
*out = wi::shwi (m_value, sizeof (m_value) * 8);
return true;
}
/* The write_reproducer specialization for <int>. */
template <>
......@@ -4123,6 +4161,16 @@ memento_of_new_rvalue_from_const <long>::make_debug_string ()
m_value);
}
/* The get_wide_int specialization for <long>. */
template <>
bool
memento_of_new_rvalue_from_const <long>::get_wide_int (wide_int *out) const
{
*out = wi::shwi (m_value, sizeof (m_value) * 8);
return true;
}
/* The write_reproducer specialization for <long>. */
template <>
......@@ -4176,6 +4224,15 @@ memento_of_new_rvalue_from_const <double>::make_debug_string ()
m_value);
}
/* The get_wide_int specialization for <double>. */
template <>
bool
memento_of_new_rvalue_from_const <double>::get_wide_int (wide_int *) const
{
return false;
}
/* The write_reproducer specialization for <double>. */
template <>
......@@ -4215,6 +4272,15 @@ memento_of_new_rvalue_from_const <void *>::make_debug_string ()
m_type->get_debug_string ());
}
/* The get_wide_int specialization for <void *>. */
template <>
bool
memento_of_new_rvalue_from_const <void *>::get_wide_int (wide_int *) const
{
return false;
}
/* Implementation of recording::memento::write_reproducer for <void *>
values. */
......@@ -5213,14 +5279,15 @@ recording::local::write_reproducer (reproducer &r)
since this vfunc must only ever be called on terminator
statements. */
int
recording::statement::get_successor_blocks (block **/*out_next1*/,
block **/*out_next2*/) const
vec <recording::block *>
recording::statement::get_successor_blocks () const
{
/* The base class implementation is for non-terminating statements,
and thus should never be called. */
gcc_unreachable ();
return 0;
vec <block *> result;
result.create (0);
return result;
}
/* Extend the default implementation of
......@@ -5429,13 +5496,14 @@ recording::conditional::replay_into (replayer *r)
A conditional jump has 2 successor blocks. */
int
recording::conditional::get_successor_blocks (block **out_next1,
block **out_next2) const
vec <recording::block *>
recording::conditional::get_successor_blocks () const
{
*out_next1 = m_on_true;
*out_next2 = m_on_false;
return 2;
vec <block *> result;
result.create (2);
result.quick_push (m_on_true);
result.quick_push (m_on_false);
return result;
}
/* Implementation of recording::memento::make_debug_string for
......@@ -5493,12 +5561,13 @@ recording::jump::replay_into (replayer *r)
An unconditional jump has 1 successor block. */
int
recording::jump::get_successor_blocks (block **out_next1,
block **/*out_next2*/) const
vec <recording::block *>
recording::jump::get_successor_blocks () const
{
*out_next1 = m_target;
return 1;
vec <block *> result;
result.create (1);
result.quick_push (m_target);
return result;
}
/* Implementation of recording::memento::make_debug_string for
......@@ -5544,11 +5613,12 @@ recording::return_::replay_into (replayer *r)
A return statement has no successor block. */
int
recording::return_::get_successor_blocks (block **/*out_next1*/,
block **/*out_next2*/) const
vec <recording::block *>
recording::return_::get_successor_blocks () const
{
return 0;
vec <block *> result;
result.create (0);
return result;
}
/* Implementation of recording::memento::make_debug_string for
......@@ -5586,6 +5656,158 @@ recording::return_::write_reproducer (reproducer &r)
r.get_identifier (get_loc ()));
}
/* The implementation of class gcc::jit::recording::case_. */
void
recording::case_::write_reproducer (reproducer &r)
{
const char *id = r.make_identifier (this, "case");
const char *fmt =
" gcc_jit_case *%s = \n"
" gcc_jit_context_new_case (%s, /*gcc_jit_context *ctxt */\n"
" %s, /* gcc_jit_rvalue *min_value */\n"
" %s, /* gcc_jit_rvalue *max_value */\n"
" %s); /* gcc_jit_block *dest_block */\n";
r.write (fmt,
id,
r.get_identifier (get_context ()),
r.get_identifier_as_rvalue (m_min_value),
r.get_identifier_as_rvalue (m_max_value),
r.get_identifier (m_dest_block));
}
recording::string *
recording::case_::make_debug_string ()
{
return string::from_printf (get_context (),
"case %s ... %s: goto %s;",
m_min_value->get_debug_string (),
m_max_value->get_debug_string (),
m_dest_block->get_debug_string ());
}
/* The implementation of class gcc::jit::recording::switch_. */
/* gcc::jit::recording::switch_'s constructor. */
recording::switch_::switch_ (block *b,
location *loc,
rvalue *expr,
block *default_block,
int num_cases,
case_ **cases)
: statement (b, loc),
m_expr (expr),
m_default_block (default_block)
{
m_cases.reserve_exact (num_cases);
for (int i = 0; i< num_cases; i++)
m_cases.quick_push (cases[i]);
}
/* Implementation of pure virtual hook recording::memento::replay_into
for recording::switch_. */
void
recording::switch_::replay_into (replayer *r)
{
auto_vec <playback::case_> pcases;
int i;
recording::case_ *rcase;
pcases.reserve_exact (m_cases.length ());
FOR_EACH_VEC_ELT (m_cases, i, rcase)
{
playback::case_ pcase (rcase->get_min_value ()->playback_rvalue (),
rcase->get_max_value ()->playback_rvalue (),
rcase->get_dest_block ()->playback_block ());
pcases.safe_push (pcase);
}
playback_block (get_block ())
->add_switch (playback_location (r),
m_expr->playback_rvalue (),
m_default_block->playback_block (),
&pcases);
}
/* Override the poisoned default implementation of
gcc::jit::recording::statement::get_successor_blocks
A switch statement has (NUM_CASES + 1) successor blocks. */
vec <recording::block *>
recording::switch_::get_successor_blocks () const
{
vec <block *> result;
result.create (m_cases.length () + 1);
result.quick_push (m_default_block);
int i;
case_ *c;
FOR_EACH_VEC_ELT (m_cases, i, c)
result.quick_push (c->get_dest_block ());
return result;
}
/* Implementation of recording::memento::make_debug_string for
a switch statement. */
recording::string *
recording::switch_::make_debug_string ()
{
auto_vec <char> cases_str;
int i;
case_ *c;
FOR_EACH_VEC_ELT (m_cases, i, c)
{
size_t len = strlen (c->get_debug_string ());
unsigned idx = cases_str.length ();
cases_str.safe_grow (idx + 1 + len);
cases_str[idx] = ' ';
memcpy (&(cases_str[idx + 1]),
c->get_debug_string (),
len);
}
cases_str.safe_push ('\0');
return string::from_printf (m_ctxt,
"switch (%s) {default: goto %s;%s}",
m_expr->get_debug_string (),
m_default_block->get_debug_string (),
&cases_str[0]);
}
/* Implementation of recording::memento::write_reproducer for
switch statements. */
void
recording::switch_::write_reproducer (reproducer &r)
{
r.make_identifier (this, "switch");
int i;
case_ *c;
const char *cases_id =
r.make_tmp_identifier ("cases_for", this);
r.write (" gcc_jit_case *%s[%i] = {\n",
cases_id,
m_cases.length ());
FOR_EACH_VEC_ELT (m_cases, i, c)
r.write (" %s,\n", r.get_identifier (c));
r.write (" };\n");
const char *fmt =
" gcc_jit_block_end_with_switch (%s, /*gcc_jit_block *block */\n"
" %s, /* gcc_jit_location *loc */\n"
" %s, /* gcc_jit_rvalue *expr */\n"
" %s, /* gcc_jit_block *default_block */\n"
" %i, /* int num_cases */\n"
" %s); /* gcc_jit_case **cases */\n";
r.write (fmt,
r.get_identifier (get_block ()),
r.get_identifier (get_loc ()),
r.get_identifier_as_rvalue (m_expr),
r.get_identifier (m_default_block),
m_cases.length (),
cases_id);
}
} // namespace gcc::jit
} // namespace gcc
......@@ -183,6 +183,11 @@ public:
rvalue *ptr,
rvalue *index);
case_ *
new_case (rvalue *min_value,
rvalue *max_value,
block *block);
void
set_str_option (enum gcc_jit_str_option opt,
const char *value);
......@@ -954,6 +959,9 @@ public:
const char *
get_debug_string_parens (enum precedence outer_prec);
virtual bool is_constant () const { return false; }
virtual bool get_wide_int (wide_int *) const { return false; }
private:
virtual enum precedence get_precedence () const = 0;
......@@ -1152,6 +1160,13 @@ public:
end_with_return (location *loc,
rvalue *rvalue);
statement *
end_with_switch (location *loc,
rvalue *expr,
block *default_block,
int num_cases,
case_ **cases);
playback::block *
playback_block () const
{
......@@ -1167,7 +1182,7 @@ public:
statement *get_first_statement () const;
statement *get_last_statement () const;
int get_successor_blocks (block **next1, block **next2) const;
vec <block *> get_successor_blocks () const;
private:
string * make_debug_string ();
......@@ -1233,6 +1248,10 @@ public:
void visit_children (rvalue_visitor *) {}
bool is_constant () const { return true; }
bool get_wide_int (wide_int *out) const;
private:
string * make_debug_string ();
void write_reproducer (reproducer &r);
......@@ -1596,8 +1615,7 @@ private:
class statement : public memento
{
public:
virtual int get_successor_blocks (block **out_next1,
block **out_next2) const;
virtual vec <block *> get_successor_blocks () const;
void write_to_dump (dump &d);
......@@ -1721,8 +1739,7 @@ public:
void replay_into (replayer *r);
int get_successor_blocks (block **out_next1,
block **out_next2) const;
vec <block *> get_successor_blocks () const;
private:
string * make_debug_string ();
......@@ -1745,8 +1762,7 @@ public:
void replay_into (replayer *r);
int get_successor_blocks (block **out_next1,
block **out_next2) const;
vec <block *> get_successor_blocks () const;
private:
string * make_debug_string ();
......@@ -1767,8 +1783,7 @@ public:
void replay_into (replayer *r);
int get_successor_blocks (block **out_next1,
block **out_next2) const;
vec <block *> get_successor_blocks () const;
private:
string * make_debug_string ();
......@@ -1778,6 +1793,60 @@ private:
rvalue *m_rvalue;
};
class case_ : public memento
{
public:
case_ (context *ctxt,
rvalue *min_value,
rvalue *max_value,
block *dest_block)
: memento (ctxt),
m_min_value (min_value),
m_max_value (max_value),
m_dest_block (dest_block)
{}
rvalue *get_min_value () const { return m_min_value; }
rvalue *get_max_value () const { return m_max_value; }
block *get_dest_block () const { return m_dest_block; }
void replay_into (replayer *) { /* empty */ }
void write_reproducer (reproducer &r);
private:
string * make_debug_string ();
private:
rvalue *m_min_value;
rvalue *m_max_value;
block *m_dest_block;
};
class switch_ : public statement
{
public:
switch_ (block *b,
location *loc,
rvalue *expr,
block *default_block,
int num_cases,
case_ **cases);
void replay_into (replayer *r);
vec <block *> get_successor_blocks () const;
private:
string * make_debug_string ();
void write_reproducer (reproducer &r);
private:
rvalue *m_expr;
block *m_default_block;
auto_vec <case_ *> m_cases;
};
} // namespace gcc::jit::recording
/* Create a recording::memento_of_new_rvalue_from_const instance and add
......
......@@ -45,6 +45,7 @@ namespace gccjit
class rvalue;
class lvalue;
class param;
class case_;
/* Errors within the API become C++ exceptions of this class. */
class error
......@@ -297,6 +298,10 @@ namespace gccjit
rvalue index,
location loc = location ());
case_ new_case (rvalue min_value,
rvalue max_value,
block dest_block);
private:
gcc_jit_context *m_inner_ctxt;
};
......@@ -421,6 +426,10 @@ namespace gccjit
location loc = location ());
void end_with_return (location loc = location ());
void end_with_switch (rvalue expr,
block default_block,
std::vector <case_> cases,
location loc = location ());
};
class rvalue : public object
......@@ -471,6 +480,14 @@ namespace gccjit
gcc_jit_param *get_inner_param () const;
};
class case_ : public object
{
public:
case_ ();
case_ (gcc_jit_case *inner);
gcc_jit_case *get_inner_case () const;
};
/* Overloaded operators, for those who want the most terse API
(at the possible risk of being a little too magical).
......@@ -1124,6 +1141,17 @@ context::new_array_access (rvalue ptr,
index.get_inner_rvalue ()));
}
inline case_
context::new_case (rvalue min_value,
rvalue max_value,
block dest_block)
{
return case_ (gcc_jit_context_new_case (m_inner_ctxt,
min_value.get_inner_rvalue (),
max_value.get_inner_rvalue (),
dest_block.get_inner_block ()));
}
// class object
inline context
object::get_context () const
......@@ -1371,6 +1399,27 @@ block::end_with_return (location loc)
loc.get_inner_location ());
}
inline void
block::end_with_switch (rvalue expr,
block default_block,
std::vector <case_> cases,
location loc)
{
/* Treat std::vector as an array, relying on it not being resized: */
case_ *as_array_of_wrappers = &cases[0];
/* Treat the array as being of the underlying pointers, relying on
the wrapper type being such a pointer internally. */
gcc_jit_case **as_array_of_ptrs =
reinterpret_cast<gcc_jit_case **> (as_array_of_wrappers);
gcc_jit_block_end_with_switch (get_inner_block (),
loc.get_inner_location (),
expr.get_inner_rvalue (),
default_block.get_inner_block (),
cases.size (),
as_array_of_ptrs);
}
inline rvalue
block::add_call (function other,
location loc)
......@@ -1561,6 +1610,20 @@ inline param::param (gcc_jit_param *inner)
: lvalue (gcc_jit_param_as_lvalue (inner))
{}
// class case_ : public object
inline case_::case_ () : object () {}
inline case_::case_ (gcc_jit_case *inner)
: object (gcc_jit_case_as_object (inner))
{
}
inline gcc_jit_case *
case_::get_inner_case () const
{
/* Manual downcast: */
return reinterpret_cast<gcc_jit_case *> (get_inner_object ());
}
/* Overloaded operators. */
// Unary operators
inline rvalue operator- (rvalue a)
......
......@@ -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
**********************************************************************/
......
......@@ -67,6 +67,7 @@ typedef struct gcc_jit_result gcc_jit_result;
+- gcc_jit_rvalue
+- gcc_jit_lvalue
+- gcc_jit_param
+- gcc_jit_case
*/
typedef struct gcc_jit_object gcc_jit_object;
......@@ -131,6 +132,12 @@ typedef struct gcc_jit_lvalue gcc_jit_lvalue;
rvalue); use gcc_jit_param_as_lvalue to convert. */
typedef struct gcc_jit_param gcc_jit_param;
/* A gcc_jit_case is for use when building multiway branches via
gcc_jit_block_end_with_switch and represents a range of integer
values (or an individual integer value) together with an associated
destination block. */
typedef struct gcc_jit_case gcc_jit_case;
/* Acquire a JIT-compilation context. */
extern gcc_jit_context *
gcc_jit_context_acquire (void);
......@@ -1097,6 +1104,81 @@ extern void
gcc_jit_block_end_with_void_return (gcc_jit_block *block,
gcc_jit_location *loc);
/* Create a new gcc_jit_case instance for use in a switch statement.
min_value and max_value must be constants of integer type.
This API entrypoint was added in LIBGCCJIT_ABI_3; you can test for its
presence using
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
*/
extern 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 *dest_block);
/* Upcasting from case to object.
This API entrypoint was added in LIBGCCJIT_ABI_3; you can test for its
presence using
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
*/
extern gcc_jit_object *
gcc_jit_case_as_object (gcc_jit_case *case_);
/* Terminate a block by adding evalation of an rvalue, then performing
a multiway branch.
This is roughly equivalent to this C code:
switch (expr)
{
default:
goto default_block;
case C0.min_value ... C0.max_value:
goto C0.dest_block;
case C1.min_value ... C1.max_value:
goto C1.dest_block;
...etc...
case C[N - 1].min_value ... C[N - 1].max_value:
goto C[N - 1].dest_block;
}
block, expr, default_block and cases must all be non-NULL.
expr must be of the same integer type as all of the min_value
and max_value within the cases.
num_cases must be >= 0.
The ranges of the cases must not overlap (or have duplicate
values).
This API entrypoint was added in LIBGCCJIT_ABI_3; you can test for its
presence using
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
*/
extern 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);
/* Pre-canned feature macro to indicate the presence of
gcc_jit_block_end_with_switch, gcc_jit_case_as_object, and
gcc_jit_context_new_case.
This can be tested for with #ifdef. */
#define LIBGCCJIT_HAVE_SWITCH_STATEMENTS
/**********************************************************************
Nested contexts.
**********************************************************************/
......
......@@ -120,3 +120,11 @@ LIBGCCJIT_ABI_2 {
global:
gcc_jit_context_set_bool_allow_unreachable_blocks;
} LIBGCCJIT_ABI_1;
# Add support for switch statements.
LIBGCCJIT_ABI_3 {
global:
gcc_jit_block_end_with_switch;
gcc_jit_case_as_object;
gcc_jit_context_new_case;
} LIBGCCJIT_ABI_2;
2015-06-30 David Malcolm <dmalcolm@redhat.com>
* 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.
2015-06-30 David Malcolm <dmalcolm@redhat.com>
PR jit/66546
* jit.dg/all-non-failing-tests.h: Add note about
test-validly-unreachable-block.c.
......
......@@ -175,6 +175,13 @@
#undef create_code
#undef verify_code
/* test-switch.c */
#define create_code create_code_switch
#define verify_code verify_code_switch
#include "test-switch.c"
#undef create_code
#undef verify_code
/* test-types.c */
#define create_code create_code_types
#define verify_code verify_code_types
......@@ -281,6 +288,9 @@ const struct testcase testcases[] = {
{"sum_of_squares",
create_code_sum_of_squares,
verify_code_sum_of_squares},
{"switch",
create_code_switch,
verify_code_switch},
{"types",
create_code_types,
verify_code_types},
......
#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
test_switch (int x)
{
switch (x)
{
case x:
return 3;
default:
return 10;
}
}
and verify that we get a sane error about the non-const
case.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_case *cases[1] = {
NULL
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
1,
cases);
}
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),
"gcc_jit_block_end_with_switch: NULL case 0");
}
#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
test_switch (int x)
{
switch (x)
{
case (long long)0 ... (long long)5:
return 3;
default:
return 10;
}
}
and verify that the floating-point case is an error.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *t_long_long =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_LONG_LONG);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_0 =
gcc_jit_function_new_block (func, "case_0");
/* Note the erroneous use of "t_float" here. */
gcc_jit_case *cases[1] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_long_long, 0),
gcc_jit_context_new_rvalue_from_int (ctxt, t_long_long, 5),
b_case_0)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
1,
cases);
gcc_jit_block_end_with_return (
b_case_0, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
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),
"gcc_jit_block_end_with_switch:"
" mismatching types between case and expression:"
" cases[0]->min_value: (long long)0 (type: long long)"
" expr: x (type: int)");
}
#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
test_switch (int x)
{
switch (x)
{
case 0 ... 5:
return 3;
case 5 ... 10:
return 4;
default:
return 10;
}
}
and verify that we get an error about the overlapping
ranges.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_0_5 =
gcc_jit_function_new_block (func, "case_0_5");
gcc_jit_block *b_case_5_10 =
gcc_jit_function_new_block (func, "case_5_10");
gcc_jit_case *cases[2] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 0),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 5),
b_case_0_5),
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 5),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10),
b_case_5_10)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
2,
cases);
gcc_jit_block_end_with_return (
b_case_0_5, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_case_5_10, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 4));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
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),
"gcc_jit_block_end_with_switch:"
" duplicate (or overlapping) cases values:"
" case 1: case (int)5 ... (int)10: goto case_5_10;"
" overlaps case (int)0 ... (int)5: goto case_0_5;");
}
#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
test_switch (int x)
{
switch (x)
{
case x:
return 3;
default:
return 10;
}
}
and verify that we get a sane error about the non-const
case.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_x =
gcc_jit_function_new_block (func, "case_x");
/* Erroneous use of non-const x for a case. */
gcc_jit_case *cases[1] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_param_as_rvalue (x),
gcc_jit_param_as_rvalue (x),
b_case_x)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
1,
cases);
gcc_jit_block_end_with_return (
b_case_x, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
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),
"gcc_jit_context_new_case:"
" min_value is not a constant: x");
}
#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
test_switch (int x)
{
switch (x)
{
case 0.f ... 5.f:
return 3;
default:
return 10;
}
}
and verify that the floating-point case is an error.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *t_float =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_FLOAT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_0 =
gcc_jit_function_new_block (func, "case_0");
/* Note the erroneous use of "t_float" here. */
gcc_jit_case *cases[1] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_float, 0),
gcc_jit_context_new_rvalue_from_int (ctxt, t_float, 5),
b_case_0)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
1,
cases);
gcc_jit_block_end_with_return (
b_case_0, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
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),
"gcc_jit_context_new_case:"
" min_value: (float)0 (type: float) is not of integer type");
}
#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
test_switch (int x)
{
switch (x)
{
case 5 ... 0:
return 3;
default:
return 10;
}
}
and verify that we get an error about the reversed endpoints
in the range.
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_5_0 =
gcc_jit_function_new_block (func, "case_5_0");
gcc_jit_case *cases[1] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 5),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 0),
b_case_5_0)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
1,
cases);
gcc_jit_block_end_with_return (
b_case_5_0, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
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),
"gcc_jit_context_new_case:"
" min_value: (int)5 > max_value: (int)0");
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libgccjit.h"
#include "harness.h"
/* Quote from here in docs/topics/functions.rst. */
void
create_code (gcc_jit_context *ctxt, void *user_data)
{
/* Let's try to inject the equivalent of:
int
test_switch (int x)
{
switch (x)
{
case 0 ... 5:
return 3;
case 25 ... 27:
return 4;
case -42 ... -17:
return 83;
case 40:
return 8;
default:
return 10;
}
}
*/
gcc_jit_type *t_int =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = t_int;
gcc_jit_param *x =
gcc_jit_context_new_param (ctxt, NULL, t_int, "x");
gcc_jit_param *params[1] = {x};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
1, params, 0);
gcc_jit_block *b_initial =
gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default =
gcc_jit_function_new_block (func, "default");
gcc_jit_block *b_case_0_5 =
gcc_jit_function_new_block (func, "case_0_5");
gcc_jit_block *b_case_25_27 =
gcc_jit_function_new_block (func, "case_25_27");
gcc_jit_block *b_case_m42_m17 =
gcc_jit_function_new_block (func, "case_m42_m17");
gcc_jit_block *b_case_40 =
gcc_jit_function_new_block (func, "case_40");
gcc_jit_case *cases[4] = {
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 0),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 5),
b_case_0_5),
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 25),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 27),
b_case_25_27),
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, -42),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, -17),
b_case_m42_m17),
gcc_jit_context_new_case (
ctxt,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 40),
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 40),
b_case_40)
};
gcc_jit_block_end_with_switch (
b_initial, NULL,
gcc_jit_param_as_rvalue (x),
b_default,
4, cases);
gcc_jit_block_end_with_return (
b_case_0_5, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3));
gcc_jit_block_end_with_return (
b_case_25_27, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 4));
gcc_jit_block_end_with_return (
b_case_m42_m17, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 83));
gcc_jit_block_end_with_return (
b_case_40, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 8));
gcc_jit_block_end_with_return (
b_default, NULL,
gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10));
}
/* Quote up to here in docs/topics/functions.rst. */
static int
c_test_switch (int x)
{
switch (x)
{
case 0 ... 5:
return 3;
case 25 ... 27:
return 4;
case -42 ... -17:
return 83;
case 40:
return 8;
default:
return 10;
}
}
void
verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
{
typedef int (*test_switch_type) (int);
CHECK_NON_NULL (result);
test_switch_type test_switch =
(test_switch_type)gcc_jit_result_get_code (result, "test_switch");
CHECK_NON_NULL (test_switch);
int i;
for (i = -255; i < 255; i++)
{
int val = test_switch (i);
int exp = c_test_switch (i);
if (val != exp)
fail ("test_switch (%i) returned: %i; expected; %i", i, val, exp);
}
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libgccjit++.h"
#include "harness.h"
/* Quote from here in docs/cp/topics/functions.rst. */
void
create_code (gcc_jit_context *c_ctxt, void *user_data)
{
/* Let's try to inject the equivalent of:
int
test_switch (int x)
{
switch (x)
{
case 0 ... 5:
return 3;
case 25 ... 27:
return 4;
case -42 ... -17:
return 83;
case 40:
return 8;
default:
return 10;
}
}
*/
gccjit::context ctxt (c_ctxt);
gccjit::type t_int = ctxt.get_type (GCC_JIT_TYPE_INT);
gccjit::type return_type = t_int;
gccjit::param x = ctxt.new_param (t_int, "x");
std::vector <gccjit::param> params;
params.push_back (x);
gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
return_type,
"test_switch",
params, 0);
gccjit::block b_initial = func.new_block ("initial");
gccjit::block b_default = func.new_block ("default");
gccjit::block b_case_0_5 = func.new_block ("case_0_5");
gccjit::block b_case_25_27 = func.new_block ("case_25_27");
gccjit::block b_case_m42_m17 = func.new_block ("case_m42_m17");
gccjit::block b_case_40 = func.new_block ("case_40");
std::vector <gccjit::case_> cases;
cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 0),
ctxt.new_rvalue (t_int, 5),
b_case_0_5));
cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 25),
ctxt.new_rvalue (t_int, 27),
b_case_25_27));
cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, -42),
ctxt.new_rvalue (t_int, -17),
b_case_m42_m17));
cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 40),
ctxt.new_rvalue (t_int, 40),
b_case_40));
b_initial.end_with_switch (x,
b_default,
cases);
b_case_0_5.end_with_return (ctxt.new_rvalue (t_int, 3));
b_case_25_27.end_with_return (ctxt.new_rvalue (t_int, 4));
b_case_m42_m17.end_with_return (ctxt.new_rvalue (t_int, 83));
b_case_40.end_with_return (ctxt.new_rvalue (t_int, 8));
b_default.end_with_return (ctxt.new_rvalue (t_int, 10));
}
/* Quote up to here in docs/cp/topics/functions.rst. */
static int
c_test_switch (int x)
{
switch (x)
{
case 0 ... 5:
return 3;
case 25 ... 27:
return 4;
case -42 ... -17:
return 83;
case 40:
return 8;
default:
return 10;
}
}
void
verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
{
typedef int (*test_switch_type) (int);
CHECK_NON_NULL (result);
test_switch_type test_switch =
(test_switch_type)gcc_jit_result_get_code (result, "test_switch");
CHECK_NON_NULL (test_switch);
int i;
for (i = -255; i < 255; i++)
{
int val = test_switch (i);
int exp = c_test_switch (i);
if (val != exp)
fail ("test_switch (%i) returned: %i; expected; %i", i, val, exp);
}
}
/* A typesafe wrapper around libiberty's splay-tree.h.
Copyright (C) 2015 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_TYPED_SPLAY_TREE_H
#define GCC_TYPED_SPLAY_TREE_H
#include "splay-tree.h"
/* Typesafe wrapper around libiberty's splay-tree.h. */
template <typename KEY_TYPE, typename VALUE_TYPE>
class typed_splay_tree
{
public:
typedef KEY_TYPE key_type;
typedef VALUE_TYPE value_type;
typedef int (*compare_fn) (key_type, key_type);
typedef void (*delete_key_fn) (key_type);
typedef void (*delete_value_fn) (value_type);
typed_splay_tree (compare_fn,
delete_key_fn,
delete_value_fn);
~typed_splay_tree ();
value_type lookup (key_type k);
value_type predecessor (key_type k);
value_type successor (key_type k);
void insert (key_type k, value_type v);
private:
static value_type node_to_value (splay_tree_node node);
private:
::splay_tree m_inner;
};
/* Constructor for typed_splay_tree <K, V>. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline typed_splay_tree<KEY_TYPE, VALUE_TYPE>::
typed_splay_tree (compare_fn compare_fn,
delete_key_fn delete_key_fn,
delete_value_fn delete_value_fn)
{
m_inner = splay_tree_new ((splay_tree_compare_fn)compare_fn,
(splay_tree_delete_key_fn)delete_key_fn,
(splay_tree_delete_value_fn)delete_value_fn);
}
/* Destructor for typed_splay_tree <K, V>. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline typed_splay_tree<KEY_TYPE, VALUE_TYPE>::
~typed_splay_tree ()
{
splay_tree_delete (m_inner);
}
/* Lookup KEY, returning a value if present, and NULL
otherwise. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline VALUE_TYPE
typed_splay_tree<KEY_TYPE, VALUE_TYPE>::lookup (key_type key)
{
splay_tree_node node = splay_tree_lookup (m_inner, (splay_tree_key)key);
return node_to_value (node);
}
/* Return the immediate predecessor of KEY, or NULL if there is no
predecessor. KEY need not be present in the tree. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline VALUE_TYPE
typed_splay_tree<KEY_TYPE, VALUE_TYPE>::predecessor (key_type key)
{
splay_tree_node node = splay_tree_predecessor (m_inner, (splay_tree_key)key);
return node_to_value (node);
}
/* Return the immediate successor of KEY, or NULL if there is no
successor. KEY need not be present in the tree. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline VALUE_TYPE
typed_splay_tree<KEY_TYPE, VALUE_TYPE>::successor (key_type k)
{
splay_tree_node node = splay_tree_successor (m_inner, (splay_tree_key)k);
return node_to_value (node);
}
/* Insert a new node (associating KEY with VALUE). If a
previous node with the indicated KEY exists, its data is replaced
with the new value. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline void
typed_splay_tree<KEY_TYPE, VALUE_TYPE>::insert (key_type key,
value_type value)
{
splay_tree_insert (m_inner,
(splay_tree_key)key,
(splay_tree_value)value);
}
/* Internal function for converting from splay_tree_node to
VALUE_TYPE. */
template <typename KEY_TYPE, typename VALUE_TYPE>
inline VALUE_TYPE
typed_splay_tree<KEY_TYPE, VALUE_TYPE>::node_to_value (splay_tree_node node)
{
if (node)
return (value_type)node->value;
else
return 0;
}
#endif /* GCC_TYPED_SPLAY_TREE_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment