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.
......
......@@ -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)
......
......@@ -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)
......
......@@ -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