public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
* Managing ABI compatibility (was Re: [PATCH 2/2] jit: add switch statements)
  2015-01-01  0:00                 ` Jeff Law
@ 2015-01-01  0:00                   ` David Malcolm
  2015-01-01  0:00                     ` David Malcolm
  0 siblings, 1 reply; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches, jit

[-- Attachment #1: Type: text/plain, Size: 3536 bytes --]

On Thu, 2015-06-25 at 13:16 -0600, Jeff Law wrote:
> On 06/25/2015 01:13 PM, David Malcolm wrote:
> >
> >    * It extends the libgccjit API.  It's not clear to me yet how to
> >      manage extensions of the libgccjit API: should I use symbol maps
> >      and versioning, or bump the SONAME?  I'm thinking of providing
> >      precanned feature macros within libgccjit.h e.g:
> >
> >        #define LIBGCCJIT_HAVE_SWITCH_STATEMENT
> Seems to me you should use a symbol map and bump the version.

Thanks.  By "the version", do you mean the version within the symbol
map?

I've read Uli Drepper's "How To Write Shared Libraries" guide:
  http://www.akkadia.org/drepper/dsohowto.pdf
and in particular section 3: "Maintaining APIs and ABIs", which suggests
versioned DSOs (though this presumably implies the use of GNU ld).

I attempted to use the symbol map to segregate the new symbols into
their own version; attached is a patch which does that (on top of the
"add switch statements" patch).

Is that the kind of thing you had in mind?

My first attempt was to keep the existing syms in an anonymous tag but I
got:
  /usr/bin/ld: anonymous version tag cannot be combined with other
version tags
(which comes from binutils ld/ldlang.c:lang_register_vers_node)

Hence I put the existing symbols into a LIBGCCJIT_ABI_1 version.

With the above change, using "eu-readelf -s" I see a client program
linked against libgccjit goes from having e.g.:

GLOBAL DEFAULT    UNDEF gcc_jit_context_compile

to having:

GLOBAL DEFAULT    UNDEF gcc_jit_context_compile@@LIBGCCJIT_ABI_1

and one using the new symbols has:

GLOBAL DEFAULT    UNDEF gcc_jit_context_compile@LIBGCCJIT_ABI_1 (2)
GLOBAL DEFAULT    UNDEF gcc_jit_block_end_with_switch@LIBGCCJIT_ABI_2 (4)


Presumably I'd need to bump the SONAME to accommodate the introduction
of using symbol versioning?  Am I right in hoping that with the
introduction to using symbol versioning that I'd never need to bump the
SONAME again?

Running:

  objdump -p client-binary-using-just-old-symbols

gives (amongst other output):

  Version References:
    required from libc.so.6:
      0x09691a75 0x00 03 GLIBC_2.2.5
    required from libgccjit.so.0:
      0x00824161 0x00 02 LIBGCCJIT_ABI_1

whereas:

  objdump -p client-binary-using-some-new-symbols

gives:

  Version References:
    required from libc.so.6:
      0x09691a75 0x00 03 GLIBC_2.2.5
    required from libgccjit.so.0:
      0x00824162 0x00 04 LIBGCCJIT_ABI_2
      0x00824161 0x00 02 LIBGCCJIT_ABI_1

FWIW objdump -p is used by /usr/lib/rpm/find-requires, so presumably
these entries would show up in rpm metadata, so that binaries using the
new symbols get flagged at the RPM level as needing a more up-to-date
libgccjit rpm.

Hopefully other packaging systems handle symbol versioning in similar
ways.

Does this approach look sane?

I'd probably add a page about ABI to the docs, and list the ABIs there.

This doesn't cover the addition of new values to the various options
enums; I'm not sure how to handle capturing *that* within the binary
metadata without bumping the SONAME.  Maybe bump the
gcc_jit_context_set_FOO_option entrypoints into new version tags each
time we add a new value to the corresponding option enum?

Would it be sane to go to a feature-based naming scheme for tags, rather
than pure numbers?
e.g.
  LIBGCCJIT_ABI_INITIAL
  LIBGCCJIT_ABI_WITH_SWITCH_STATEMENTS
  LIGCCCJIT_ABI_WITH_BOOL_OPTION_ERROR_ON_UNREACHABLE_BLOCKS
etc (how long can they be, and is there a cost? can they include lower
case?)

Dave

[-- Attachment #2: add-symbol-versioning.patch --]
[-- Type: text/x-patch, Size: 1319 bytes --]

diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 93b19e8..7bf89f2 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -17,6 +17,9 @@
 # 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/>.  */
+
+# The initial release of the library.
+LIBGCCJIT_ABI_1
 {
   global:
     # Keep this list sorted alphabetically:
@@ -29,9 +32,7 @@
     gcc_jit_block_end_with_jump;
     gcc_jit_block_end_with_return;
     gcc_jit_block_end_with_void_return;
-    gcc_jit_block_end_with_switch;
     gcc_jit_block_get_function;
-    gcc_jit_case_as_object;
     gcc_jit_context_acquire;
     gcc_jit_context_add_option;
     gcc_jit_context_compile;
@@ -49,7 +50,6 @@
     gcc_jit_context_new_binary_op;
     gcc_jit_context_new_call;
     gcc_jit_context_new_call_through_ptr;
-    gcc_jit_context_new_case;
     gcc_jit_context_new_cast;
     gcc_jit_context_new_child_context;
     gcc_jit_context_new_comparison;
@@ -109,4 +109,12 @@
     gcc_jit_type_get_volatile;
 
   local: *;
-};
\ No newline at end of file
+};
+
+# Add support for switch statements.
+LIBGCCJIT_ABI_2 {
+  global:
+    gcc_jit_block_end_with_switch;
+    gcc_jit_case_as_object;
+    gcc_jit_context_new_case;
+} LIBGCCJIT_ABI_1;

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/2] Add gcc/typed-splay-tree.h
  2015-01-01  0:00               ` [PATCH 1/2] Add gcc/typed-splay-tree.h Jeff Law
@ 2015-01-01  0:00                 ` David Malcolm
  2015-01-01  0:00                   ` Jeff Law
  0 siblings, 1 reply; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches, jit

[-- Attachment #1: Type: text/plain, Size: 1235 bytes --]

On Thu, 2015-06-25 at 13:18 -0600, Jeff Law wrote:
> On 06/25/2015 01:13 PM, David Malcolm wrote:
> > I found when implementing switch statements for the jit that it
> > was much easier to work with libiberty's splay-tree.h by first
> > wrapping it in a C++ wrapper to add typesafety.
> >
> > This patch adds such a wrapper, implementing the methods I needed.
> >
> > It's unused in this patch, but is used in the followup patch (which only
> > touches the jit).
> >
> > OK for trunk?
> >
> > gcc/ChangeLog:
> > 	* typed-splay-tree.h: New file.
> OK.

Well, this is embarrassing, it seems the patch I posted to the list
doesn't actually compile.

The underlying splay_tree_insert returns a splay_tree_node, I had the
typed_splay_tree::insert returning a value_type.  I dropped this bogus
return type from the "insert" method in the implementation, in favor of
"void", but I forgot to update the declaration, leading to errors when
attempting to actually compile this (via jit/jit-recording.c in the
followup patch).

The attached one-liner patch drops it from the declaration, and applies
to [PATCH 1/2].  I don't know if I can count this as "obvious"...  It
does compile now, and "make check-jit" looks good.

Suitably mortified
Dave

[-- Attachment #2: fix-typed-splay-tree.h --]
[-- Type: text/x-chdr, Size: 634 bytes --]

commit 839fdd693d2fe8d12f04ab5bde20e8595b3031f2
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Fri Jun 26 05:22:32 2015 -0400

    fix typed-splay-tree.h insert

diff --git a/gcc/typed-splay-tree.h b/gcc/typed-splay-tree.h
index 1ec4894..7849862 100644
--- a/gcc/typed-splay-tree.h
+++ b/gcc/typed-splay-tree.h
@@ -42,7 +42,7 @@ class typed_splay_tree
   value_type lookup (key_type k);
   value_type predecessor (key_type k);
   value_type successor (key_type k);
-  value_type insert (key_type k, value_type v);
+  void insert (key_type k, value_type v);
 
  private:
   static value_type node_to_value (splay_tree_node node);

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00   ` Basile Starynkevitch
@ 2015-01-01  0:00     ` David Malcolm
  2015-01-01  0:00       ` Dibyendu Majumdar
                         ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Basile Starynkevitch; +Cc: jit

On Tue, 2015-06-23 at 00:02 +0200, Basile Starynkevitch wrote:
> On 06/22/2015 11:42 PM, David Malcolm wrote:
> > On Mon, 2015-06-22 at 23:40 +0200, Basile Starynkevitch wrote:
> >> Hello David & all
> >>
> >> I'm guessing that GCCJIT is able to emit switch like statements, e.g.
> >> using GIMPLE_SWITCH statements
> >> internally.
> > No, it doesn't.
> >
> >> But I don't understand how is it possible. It looks like
> >> gimple_build_switch does not occur in gcc/jit/
> > Currently gcc/jit builds functions at the tree level and hands them off
> > to the gimplifier, so you wouldn't see that in any case.  (in theory it
> > could be ported to directly generate gimple).
> >
> >> Are switch statements omitted from GCCJIT?
> > Yes.
> >
> >> If yes, why???
> > I intentionally didn't implement them, to keep the API simpler.
> >
> > I've never run into a need for them when implementing jit-compilation,
> > and in theory they could be implemented using conditionals (albeit
> > without the nice optimizations that we have for lowering GIMPLE_SWITCH).
> >
> > There was some discussion about this here:
> >   https://gcc.gnu.org/ml/jit/2014-q4/msg00116.html
> >
> >> David, do you intend to improve that?
> > Do you have a use-case for them?  We can add them if we need them.
> 
> Any language (MELT, Ocaml, Haskell, ....) having some pattern matching 
> would use a lot of switches.
> Or most efficient implementations of Rete algorithm, or similar stuff 
> when compiling Prolog-like or CLIPS-like rules.
> 
> Also, translation of most finite state automatons is done by a switch 
> (often a quite big one, with one case per each state).
> 
> At last, any kind of "byte-code" interpreter uses switches.
> 
> And many languages have a switch like construct, that would be trivial 
> to translate to a GIMPLE_SWITCH, but painful to translate otherwise.
> 
> 
> All the bytecodes I know (e.g. JVM & Ocaml) have switch-like constructs, 
> and it would be easy to translate them to a GIMPLE_SWITCH, and painful 
> otherwise.

As it happens, none of the bytecode languages I've implemented so far
have switch-like constructs.

But I see now that the JVM has opcodes "lookupswitch" and "tableswitch";
those alone make a compelling case for libgccjit supporting switches.

I'm working on it now; the API I'm thinking of looks like this:

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_rvalue **case_min_values,
			          gcc_jit_rvalue **case_max_values,
			          gcc_jit_block **case_blocks);


thus supporting ranged cases, whilst also allowing individual values, by
simply passing in the same table for both case_min_values and
case_max_values.

> BTW, if we don't have switches, we should at least have indirect jumps, 
> and the ability to retrieve, as a label, the starting address of any 
> basic block. (i.e. the equivalent of goto *ptr; and of &&label in C). If 
> that is possible today, we need more documentation about that (at least 
> saying that switch statements could be translated that way)
> 
> And of course, leveraging on all the important optimizations done by GCC 
> on GIMPLE_SWITCH is essential...
> 
> Actually, I'm surprised you are asking what is the use case for 
> switches. I feel they are obvious and numerous... I would be annoyed, 
> e.g. if I could not use switches in my C++ or C code. Replacing a switch 
> with a sequence of if is an annoyance, and is probably a major 
> performance loss (unless GCC optimizations are clever enough to replace 
> them with a switch; which might sometimes be true, but not always).


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00         ` David Malcolm
@ 2015-01-01  0:00           ` Dibyendu Majumdar
  2015-01-01  0:00           ` Basile Starynkevitch
  1 sibling, 0 replies; 23+ messages in thread
From: Dibyendu Majumdar @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: Basile Starynkevitch, jit

On 24 June 2015 at 01:40, David Malcolm <dmalcolm@redhat.com> wrote:
>> The API looks ok to me but would it be more user friendly if you
>> created a struct to hold the case values and blocks together? That is:
>>
>> struct case {
>>   gcc_jit_rvalue *min_value;
>>   gcc_jit_rvalue *max_value;
>>   gcc_jit_block *block;
>> };
>>
>> Then the last three parameters could be replaced by 'struct case *'.
>
> Is is actually easier that way?
>
> If so, it would probably need to be "struct gcc_jit_case", both for the
> sake of namespacing, and because "case" is a reserved word.

Yes of course, my bad.

>
> That said, it would be the first non-opaque struct in the API, which
> makes me nervous; I guess we could make it opaque like this:
>
> 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 *block);
>
> though I don't know if that's better.
>

Maybe in this case it doesn't need to be opaque as it is not a
libgccjit construct - in the sense that it is only there as a
convenience to pass parameters in.


> I prefer the original API, fwiw (with 3 arrays), as it doesn't introduce
> a new type.

Ok by me - I don't really know which one will be better in the long
run, it will only become apparent with use.

>
>> >> BTW, if we don't have switches, we should at least have indirect jumps,
>> >> and the ability to retrieve, as a label, the starting address of any
>> >> basic block. (i.e. the equivalent of goto *ptr; and of &&label in C). If
>> >> that is possible today, we need more documentation about that (at least
>> >> saying that switch statements could be translated that way)
>> >>
>>
>> It would also be useful to have the indirect jump capability - LLVM
>> offers this and I used it in Ravi to help with performance (although
>> in the particular use case I did not really see much benefit).
>
> This isn't yet supported by libgccjit.
>
> That said, what use-cases are you thinking of?   The one I'm familiar
> with is threaded dispatch of the big switch statement typically seen in
> an interpreter, but presumably anyone using libgccjit is unrolling the
> switch statement into code.

Agree, that is one of the reasons for creating a JIT in the first
place - to get rid of the switch. I used indirect branches in the
numeric for loop construct. The problem here was that Lua allows the
loop index to be either integer or floating, and the step can be
negative or positive. The original Lua bytecode has branching logic to
deal with this, but at the start of the loop it is known what the
index looks like. So then when it comes to looping rather than
rechecking the type and direction, one can use an indirect branch
which was setup at the beginning of the loop.

I found that this improves things but not very much. In the end I also
implemented specialized bytecodes for integer index, and positive step
- as this is the most common use case. The optimizer deals much better
with this - and as you saw, can recognize the loop and eliminate it
altogether. Not so with the indirect branching version.

So right now I have no need of this for Ravi - hence not a priority
from my point of view.

BTW I also do not need exception handling for Ravi as Lua uses
longjmp/setjmp for this. But other languages (including Python) need
it I think.

Another thing that many implementations need is support for garbage
collection. Again for Ravi I am fortunate that because Lua uses its
own "stack" - and the runtime manages GC of objects on that stack, I
do not need to worry about it.

Regards
Dibyendu

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00                 ` [PATCH 2/2] " Basile Starynkevitch
@ 2015-01-01  0:00                   ` David Malcolm
  0 siblings, 0 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Basile Starynkevitch; +Cc: jit

On Tue, 2015-06-30 at 12:43 +0200, Basile Starynkevitch wrote:
> On Thu, Jun 25, 2015 at 03:13:41PM -0400, David Malcolm wrote:
> > Some interpreters/VMs support a switch statement (for example the JVM
> > has opcodes "lookupswitch" and "tableswitch").  GCC has a set of
> > optimizations for efficiently handling switch statements, so it makes
> > sense to directly expose switch statements in the libgccjit API.
> > 
> > This patch implements a switch statement, but it isn't quite ready for
> > committing to trunk yet:
> > 
> 
> 
> Is that patch available (thru git or svn) on some branch?

No, but I hope to commit it to trunk today or tomorrow.

> I did not found any *jit* branch on svn://gcc.gnu.org/svn/gcc/branches

The original jit work was done in git, rather than svn, in the git
branch "dmalcolm/jit":
 https://gcc.gnu.org/git/?p=gcc.git;a=log;h=dmalcolm/jit
but that work was merged into svn trunk in November 2014.

> Do you believe it will get into GCC 6 (probably yes)?

Yes.

> Do you believe it would get into GCC 5.2 (unfortunately, perhaps no)?

I hope to do so.

> Do you think it could easily be backported into GCC 5.x ?

Probably.

Dave

^ permalink raw reply	[flat|nested] 23+ messages in thread

* switches in GCC JIT?
@ 2015-01-01  0:00 Basile Starynkevitch
  2015-01-01  0:00 ` David Malcolm
  0 siblings, 1 reply; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: jit, David Malcolm

Hello David & all

I'm guessing that GCCJIT is able to emit switch like statements, e.g. 
using GIMPLE_SWITCH statements
internally.

But I don't understand how is it possible. It looks like 
gimple_build_switch does not occur in gcc/jit/

Are switch statements omitted from GCCJIT? If yes, why??? David, do you 
intend to improve that?

Regards.

-- 
Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mine, sont seulement les miennes} ***

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH, committed] jit: add switch statements
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
  2015-01-01  0:00                 ` Basile Starynkevitch
@ 2015-01-01  0:00                 ` David Malcolm
  2015-01-01  0:00                 ` [PATCH 2/2] " Basile Starynkevitch
  2015-01-01  0:00                 ` Jeff Law
  3 siblings, 0 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: jit, gcc-patches; +Cc: David Malcolm

This is a revised, combined version of the patches posted here:
  https://gcc.gnu.org/ml/gcc-patches/2015-06/msg01858.html
    ("[PATCH 1/2] Add gcc/typed-splay-tree.h")

  https://gcc.gnu.org/ml/gcc-patches/2015-06/msg01859.html
    ("[PATCH 2/2] jit: add switch statements")

with the fix to patch 1/2 from:
  https://gcc.gnu.org/ml/gcc-patches/2015-06/msg02103.html

The patch extends the C and C++ APIs (with a new symbol tag
LIBGCCJIT_ABI_3), adding documentation and testcases.  I also
updated jit-playback.c's add_case to set DECL_CONTEXT on the
artificial label, preventing an error in verify_gimple_label
when configured with checking enabled.

Tested with "make check-jit"; jit.sum goes from 8054 to 8234 passes.

Committed to trunk as r225207.

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.
---
 gcc/jit/docs/cp/topics/functions.rst               |  82 ++++-
 gcc/jit/docs/cp/topics/objects.rst                 |   1 +
 gcc/jit/docs/topics/compatibility.rst              |  14 +-
 gcc/jit/docs/topics/functions.rst                  |  96 +++++-
 gcc/jit/docs/topics/objects.rst                    |   1 +
 gcc/jit/jit-common.h                               |   2 +
 gcc/jit/jit-playback.c                             |  74 ++++
 gcc/jit/jit-playback.h                             |  21 ++
 gcc/jit/jit-recording.c                            | 322 ++++++++++++++---
 gcc/jit/jit-recording.h                            |  87 ++++-
 gcc/jit/libgccjit++.h                              |  63 ++++
 gcc/jit/libgccjit.c                                | 381 +++++++++++++++++++++
 gcc/jit/libgccjit.h                                |  82 +++++
 gcc/jit/libgccjit.map                              |   8 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h       |  10 +
 ...error-gcc_jit_block_end_with_switch-NULL-case.c |  66 ++++
 ...t_block_end_with_switch-mismatching-case-type.c |  83 +++++
 ..._jit_block_end_with_switch-overlapping-ranges.c |  95 +++++
 ...rror-gcc_jit_context_new_case-non-const-label.c |  80 +++++
 ...ror-gcc_jit_context_new_case-non-integer-type.c |  81 +++++
 ...r-gcc_jit_context_new_case-reversed-endpoints.c |  80 +++++
 gcc/testsuite/jit.dg/test-switch.c                 | 147 ++++++++
 gcc/testsuite/jit.dg/test-switch.cc                | 118 +++++++
 gcc/typed-splay-tree.h                             | 135 ++++++++
 24 files changed, 2067 insertions(+), 62 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
 create mode 100644 gcc/testsuite/jit.dg/test-switch.c
 create mode 100644 gcc/testsuite/jit.dg/test-switch.cc
 create mode 100644 gcc/typed-splay-tree.h

diff --git a/gcc/jit/docs/cp/topics/functions.rst b/gcc/jit/docs/cp/topics/functions.rst
index de3570a..57b6298 100644
--- a/gcc/jit/docs/cp/topics/functions.rst
+++ b/gcc/jit/docs/cp/topics/functions.rst
@@ -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++
diff --git a/gcc/jit/docs/cp/topics/objects.rst b/gcc/jit/docs/cp/topics/objects.rst
index 714b645..8d99bd4 100644
--- a/gcc/jit/docs/cp/topics/objects.rst
+++ b/gcc/jit/docs/cp/topics/objects.rst
@@ -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:
 
diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 91bbb05..37e2866 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -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`
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index 35e58d3..2b06f60 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -161,7 +161,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.
@@ -342,3 +343,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
diff --git a/gcc/jit/docs/topics/objects.rst b/gcc/jit/docs/topics/objects.rst
index 3ae6309..85c783c 100644
--- a/gcc/jit/docs/topics/objects.rst
+++ b/gcc/jit/docs/topics/objects.rst
@@ -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`:
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index edf8d2c..3397215 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -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. */
 }
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 95ebfe0..18ae55f 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -1578,6 +1578,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::
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index a6de566..13cc748 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -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)
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index ad13aaa..a653205 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -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
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index a9bcbb5..acd69e9 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -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
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index cbdc96f..01579bd 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -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)
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 7e0bfa6..4d7dd8c 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "opts.h"
 #include "safe-ctype.h"
+#include "typed-splay-tree.h"
 
 #include "libgccjit.h"
 #include "jit-common.h"
@@ -84,6 +85,10 @@ struct gcc_jit_param : public gcc::jit::recording::param
 {
 };
 
+struct gcc_jit_case : public gcc::jit::recording::case_
+{
+};
+
 /**********************************************************************
  Error-handling.
 
@@ -2123,6 +2128,382 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block,
   block->end_with_return (loc, NULL);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::new_case method in
+   jit-recording.c.  */
+
+gcc_jit_case *
+gcc_jit_context_new_case (gcc_jit_context *ctxt,
+			  gcc_jit_rvalue *min_value,
+			  gcc_jit_rvalue *max_value,
+			  gcc_jit_block *block)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  RETURN_NULL_IF_FAIL (min_value, ctxt, NULL, "NULL min_value");
+  RETURN_NULL_IF_FAIL (max_value, ctxt, NULL, "NULL max_value");
+  RETURN_NULL_IF_FAIL (block, ctxt, NULL, "NULL block");
+
+  RETURN_NULL_IF_FAIL_PRINTF1 (min_value->is_constant (), ctxt, NULL,
+			       "min_value is not a constant: %s",
+			       min_value->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF1 (max_value->is_constant (), ctxt, NULL,
+			       "max_value is not a constant: %s",
+			       max_value->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    min_value->get_type ()->is_int (),
+    ctxt, NULL,
+    "min_value: %s (type: %s) is not of integer type",
+    min_value->get_debug_string (),
+    min_value->get_type ()->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    max_value->get_type ()->is_int (),
+    ctxt, NULL,
+    "max_value: %s (type: %s) is not of integer type",
+    max_value->get_debug_string (),
+    max_value->get_type ()->get_debug_string ());
+
+  wide_int wi_min, wi_max;
+  if (!min_value->get_wide_int (&wi_min))
+    gcc_unreachable ();
+  if (!max_value->get_wide_int (&wi_max))
+    gcc_unreachable ();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    wi::les_p (wi_min, wi_max),
+    ctxt, NULL,
+    "min_value: %s > max_value: %s",
+    min_value->get_debug_string (),
+    max_value->get_debug_string ());
+  return (gcc_jit_case *)ctxt->new_case (min_value,
+					 max_value,
+					 block);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, this calls the trivial
+   gcc::jit::recording::memento::as_object method (a case is a
+   memento), in jit-recording.h.  */
+
+gcc_jit_object *
+gcc_jit_case_as_object (gcc_jit_case *case_)
+{
+  RETURN_NULL_IF_FAIL (case_, NULL, NULL, "NULL case");
+
+  return static_cast <gcc_jit_object *> (case_->as_object ());
+}
+
+/* Helper function for gcc_jit_block_end_with_switch and
+   valid_case_for_switch.  */
+
+static bool
+valid_dest_for_switch (gcc::jit::recording::context *ctxt,
+		       gcc_jit_location *loc,
+		       const char *api_funcname,
+		       gcc::jit::recording::block *switch_block,
+		       gcc::jit::recording::block *dest_block,
+		       const char *dest_block_desc)
+{
+  if (!dest_block)
+    {
+      jit_error (ctxt, loc, "%s: NULL %s", api_funcname, dest_block_desc);
+      return false;
+    }
+  gcc::jit::recording::function *switch_fn = switch_block->get_function ();
+  gcc::jit::recording::function *dest_fn = dest_block->get_function ();
+  if (switch_fn != dest_fn)
+    {
+      jit_error (ctxt, loc,
+		 "%s: %s is not in same function:"
+		 " switch block %s is in function %s"
+		 " whereas %s %s is in function %s",
+		 api_funcname,
+		 dest_block_desc,
+		 switch_block->get_debug_string (),
+		 switch_fn->get_debug_string (),
+		 dest_block_desc,
+		 dest_block->get_debug_string (),
+		 dest_fn->get_debug_string ());
+      return false;
+    }
+  return true;
+}
+
+/* Helper function for gcc_jit_block_end_with_switch.  */
+
+static bool
+valid_case_for_switch (gcc::jit::recording::context *ctxt,
+		       gcc_jit_location *loc,
+		       const char *api_funcname,
+		       gcc_jit_block *switch_block,
+		       gcc_jit_rvalue *expr,
+		       gcc_jit_case *case_,
+		       const char *case_desc,
+		       int case_idx)
+{
+  if (!case_)
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " NULL case %i",
+		 api_funcname,
+		 case_idx);
+      return false;
+    }
+  if (!valid_dest_for_switch (ctxt, loc,
+			      api_funcname,
+			      switch_block,
+			      case_->get_dest_block (),
+			      case_desc))
+    return false;
+  gcc::jit::recording::type *expr_type = expr->get_type ();
+  if (expr_type != case_->get_min_value ()->get_type ())
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " mismatching types between case and expression:"
+		 " cases[%i]->min_value: %s (type: %s)"
+		 " expr: %s (type: %s)",
+		 api_funcname,
+		 case_idx,
+		 case_->get_min_value ()->get_debug_string (),
+		 case_->get_min_value ()->get_type ()->get_debug_string (),
+		 expr->get_debug_string (),
+		 expr_type->get_debug_string ());
+      return false;
+    }
+  if (expr_type != case_->get_max_value ()->get_type ())
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " mismatching types between case and expression:"
+		 " cases[%i]->max_value: %s (type: %s)"
+		 " expr: %s (type: %s)",
+		 api_funcname,
+		 case_idx,
+		 case_->get_max_value ()->get_debug_string (),
+		 case_->get_max_value ()->get_type ()->get_debug_string (),
+		 expr->get_debug_string (),
+		 expr_type->get_debug_string ());
+      return false;
+    }
+  return true;
+}
+
+/* A class for holding the data we need to perform error-checking
+   on a libgccjit API call.  */
+
+class api_call_validator
+{
+ public:
+  api_call_validator (gcc::jit::recording::context *ctxt,
+		      gcc_jit_location *loc,
+		      const char *funcname)
+  : m_ctxt (ctxt),
+    m_loc (loc),
+    m_funcname (funcname)
+  {}
+
+ protected:
+  gcc::jit::recording::context *m_ctxt;
+  gcc_jit_location *m_loc;
+  const char *m_funcname;
+};
+
+/* A class for verifying that the ranges of cases within
+   gcc_jit_block_end_with_switch don't overlap.  */
+
+class case_range_validator : public api_call_validator
+{
+ public:
+  case_range_validator (gcc::jit::recording::context *ctxt,
+			gcc_jit_location *loc,
+			const char *funcname);
+
+  bool
+  validate (gcc_jit_case *case_, int idx);
+
+ private:
+  static int
+  case_compare (gcc::jit::recording::rvalue *k1,
+		gcc::jit::recording::rvalue *k2);
+
+  static wide_int
+  get_wide_int (gcc::jit::recording::rvalue *k);
+
+ private:
+  typed_splay_tree <gcc::jit::recording::rvalue *, gcc_jit_case *> m_cases;
+};
+
+/* case_range_validator's ctor.  */
+
+case_range_validator::case_range_validator (gcc::jit::recording::context *ctxt,
+					    gcc_jit_location *loc,
+					    const char *funcname)
+: api_call_validator (ctxt, loc, funcname),
+  m_cases (case_compare, NULL, NULL)
+{
+}
+
+/* Ensure that the range of CASE_ does not overlap with any of the
+   ranges of cases we've already seen.
+   Return true if everything is OK.
+   Return false and emit an error if there is an overlap.
+   Compare with c-family/c-common.c:c_add_case_label.  */
+
+bool
+case_range_validator::validate (gcc_jit_case *case_,
+				int case_idx)
+{
+  /* Look up the LOW_VALUE in the table of case labels we already
+     have.  */
+  gcc_jit_case *other = m_cases.lookup (case_->get_min_value ());
+
+  /* If there was not an exact match, check for overlapping ranges.  */
+  if (!other)
+    {
+      gcc_jit_case *pred;
+      gcc_jit_case *succ;
+
+      /* Even though there wasn't an exact match, there might be an
+	 overlap between this case range and another case range.
+	 Since we've (inductively) not allowed any overlapping case
+	 ranges, we simply need to find the greatest low case label
+	 that is smaller that CASE_MIN_VALUE, and the smallest low case
+	 label that is greater than CASE_MAX_VALUE.  If there is an overlap
+	 it will occur in one of these two ranges.  */
+      pred = m_cases.predecessor (case_->get_min_value ());
+      succ = m_cases.successor (case_->get_max_value ());
+
+      /* Check to see if the PRED overlaps.  It is smaller than
+	 the LOW_VALUE, so we only need to check its max value.  */
+      if (pred)
+	{
+	  wide_int wi_case_min = get_wide_int (case_->get_min_value ());
+	  wide_int wi_pred_max = get_wide_int (pred->get_max_value ());
+	  if (wi::ges_p (wi_pred_max, wi_case_min))
+	    other = pred;
+	}
+
+      if (!other && succ)
+	{
+	  /* Check to see if the SUCC overlaps.  The low end of that
+	     range is bigger than the low end of the current range.  */
+	  wide_int wi_case_max = get_wide_int (case_->get_max_value ());
+	  wide_int wi_succ_min = get_wide_int (succ->get_min_value ());
+	  if (wi::les_p (wi_succ_min, wi_case_max))
+	    other = succ;
+	}
+    }
+
+  /* If there was an overlap, issue an error.  */
+  if (other)
+    {
+      jit_error (m_ctxt, m_loc,
+		 "%s: duplicate (or overlapping) cases values:"
+		 " case %i: %s overlaps %s",
+		 m_funcname,
+		 case_idx,
+		 case_->get_debug_string (),
+		 other->get_debug_string ());
+      return false;
+    }
+
+  /* Register this case label in the splay tree.  */
+  m_cases.insert (case_->get_min_value (),
+		  case_);
+  return true;
+}
+
+/* Compare with c-family/c-common.c:case_compare, which acts on tree
+   nodes, rather than rvalue *.
+
+   Comparator for case label values.  K1 and K2 must be constant integer
+   values (anything else should have been rejected by
+   gcc_jit_context_new_case.
+
+   Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after
+   K2, and 0 if K1 and K2 are equal.  */
+
+int
+case_range_validator::case_compare (gcc::jit::recording::rvalue * k1,
+				    gcc::jit::recording::rvalue * k2)
+{
+  wide_int wi1 = get_wide_int (k1);
+  wide_int wi2 = get_wide_int (k2);
+  return wi::cmps(wi1, wi2);
+}
+
+/* Given a const int rvalue K, get the underlying value as a wide_int.  */
+
+wide_int
+case_range_validator::get_wide_int (gcc::jit::recording::rvalue *k)
+{
+  wide_int wi;
+  bool got_wi = k->get_wide_int (&wi);
+  gcc_assert (got_wi);
+  return wi;
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::end_with_switch method in
+   jit-recording.c.  */
+
+void
+gcc_jit_block_end_with_switch (gcc_jit_block *block,
+			       gcc_jit_location *loc,
+			       gcc_jit_rvalue *expr,
+			       gcc_jit_block *default_block,
+			       int num_cases,
+			       gcc_jit_case **cases)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_IF_FAIL (expr, ctxt, loc,
+		  "NULL expr");
+  gcc::jit::recording::type *expr_type = expr->get_type ();
+  RETURN_IF_FAIL_PRINTF2 (
+    expr_type->is_int (),
+    ctxt, loc,
+    "expr: %s (type: %s) is not of integer type",
+    expr->get_debug_string (),
+    expr_type->get_debug_string ());
+  if (!valid_dest_for_switch (ctxt, loc,
+			      __func__,
+			      block,
+			      default_block,
+			      "default_block"))
+    return;
+  RETURN_IF_FAIL (num_cases >= 0, ctxt, loc, "num_cases < 0");
+  case_range_validator crv (ctxt, loc, __func__);
+  for (int i = 0; i < num_cases; i++)
+    {
+      char case_desc[32];
+      snprintf (case_desc, sizeof (case_desc),
+		"cases[%i]", i);
+      if (!valid_case_for_switch (ctxt, loc,
+				  __func__,
+				  block,
+				  expr,
+				  cases[i],
+				  case_desc,
+				  i))
+	return;
+      if (!crv.validate (cases[i], i))
+	return;
+    }
+
+  block->end_with_switch (loc, expr, default_block,
+			  num_cases,
+			  (gcc::jit::recording::case_ **)cases);
+}
+
 /**********************************************************************
  Option-management
  **********************************************************************/
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 7085b41..98b53f6 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -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.
  **********************************************************************/
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index b55df1e..3c0a0c1 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -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;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 7034c77..21ff428 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -182,6 +182,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
@@ -291,6 +298,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},
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
new file mode 100644
index 0000000..07a9848
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
@@ -0,0 +1,66 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
new file mode 100644
index 0000000..cc907ce
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
@@ -0,0 +1,83 @@
+#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)");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
new file mode 100644
index 0000000..40655c2
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
@@ -0,0 +1,95 @@
+#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;");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
new file mode 100644
index 0000000..3953818
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
@@ -0,0 +1,80 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
new file mode 100644
index 0000000..5d44286
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
@@ -0,0 +1,81 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
new file mode 100644
index 0000000..a84d9f3
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
@@ -0,0 +1,80 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-switch.c b/gcc/testsuite/jit.dg/test-switch.c
new file mode 100644
index 0000000..74088c8
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-switch.c
@@ -0,0 +1,147 @@
+#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);
+    }
+}
diff --git a/gcc/testsuite/jit.dg/test-switch.cc b/gcc/testsuite/jit.dg/test-switch.cc
new file mode 100644
index 0000000..862f7a8
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-switch.cc
@@ -0,0 +1,118 @@
+#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);
+    }
+}
diff --git a/gcc/typed-splay-tree.h b/gcc/typed-splay-tree.h
new file mode 100644
index 0000000..7849862
--- /dev/null
+++ b/gcc/typed-splay-tree.h
@@ -0,0 +1,135 @@
+/* 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  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00     ` David Malcolm
  2015-01-01  0:00       ` Dibyendu Majumdar
  2015-01-01  0:00       ` switches in GCC JIT? Basile Starynkevitch
@ 2015-01-01  0:00       ` David Malcolm
  2 siblings, 0 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Basile Starynkevitch; +Cc: jit

On Mon, 2015-06-22 at 20:25 -0400, David Malcolm wrote:
> On Tue, 2015-06-23 at 00:02 +0200, Basile Starynkevitch wrote:
> > On 06/22/2015 11:42 PM, David Malcolm wrote:
> > > On Mon, 2015-06-22 at 23:40 +0200, Basile Starynkevitch wrote:
> > >> Hello David & all
> > >>
> > >> I'm guessing that GCCJIT is able to emit switch like statements, e.g.
> > >> using GIMPLE_SWITCH statements
> > >> internally.
> > > No, it doesn't.
> > >
> > >> But I don't understand how is it possible. It looks like
> > >> gimple_build_switch does not occur in gcc/jit/
> > > Currently gcc/jit builds functions at the tree level and hands them off
> > > to the gimplifier, so you wouldn't see that in any case.  (in theory it
> > > could be ported to directly generate gimple).
> > >
> > >> Are switch statements omitted from GCCJIT?
> > > Yes.
> > >
> > >> If yes, why???
> > > I intentionally didn't implement them, to keep the API simpler.
> > >
> > > I've never run into a need for them when implementing jit-compilation,
> > > and in theory they could be implemented using conditionals (albeit
> > > without the nice optimizations that we have for lowering GIMPLE_SWITCH).
> > >
> > > There was some discussion about this here:
> > >   https://gcc.gnu.org/ml/jit/2014-q4/msg00116.html
> > >
> > >> David, do you intend to improve that?
> > > Do you have a use-case for them?  We can add them if we need them.
> > 
> > Any language (MELT, Ocaml, Haskell, ....) having some pattern matching 
> > would use a lot of switches.
> > Or most efficient implementations of Rete algorithm, or similar stuff 
> > when compiling Prolog-like or CLIPS-like rules.
> > 
> > Also, translation of most finite state automatons is done by a switch 
> > (often a quite big one, with one case per each state).
> > 
> > At last, any kind of "byte-code" interpreter uses switches.
> > 
> > And many languages have a switch like construct, that would be trivial 
> > to translate to a GIMPLE_SWITCH, but painful to translate otherwise.
> > 
> > 
> > All the bytecodes I know (e.g. JVM & Ocaml) have switch-like constructs, 
> > and it would be easy to translate them to a GIMPLE_SWITCH, and painful 
> > otherwise.
> 
> As it happens, none of the bytecode languages I've implemented so far
> have switch-like constructs.
> 
> But I see now that the JVM has opcodes "lookupswitch" and "tableswitch";
> those alone make a compelling case for libgccjit supporting switches.
"those by themselves", I should say.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/2] Add gcc/typed-splay-tree.h
  2015-01-01  0:00           ` Basile Starynkevitch
@ 2015-01-01  0:00             ` David Malcolm
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
  2015-01-01  0:00               ` [PATCH 1/2] Add gcc/typed-splay-tree.h Jeff Law
  0 siblings, 2 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: gcc-patches, jit; +Cc: David Malcolm

I found when implementing switch statements for the jit that it
was much easier to work with libiberty's splay-tree.h by first
wrapping it in a C++ wrapper to add typesafety.

This patch adds such a wrapper, implementing the methods I needed.

It's unused in this patch, but is used in the followup patch (which only
touches the jit).

OK for trunk?

gcc/ChangeLog:
	* typed-splay-tree.h: New file.
---
 gcc/typed-splay-tree.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 gcc/typed-splay-tree.h

diff --git a/gcc/typed-splay-tree.h b/gcc/typed-splay-tree.h
new file mode 100644
index 0000000..1ec4894
--- /dev/null
+++ b/gcc/typed-splay-tree.h
@@ -0,0 +1,135 @@
+/* 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);
+  value_type 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  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
@ 2015-01-01  0:00                 ` Basile Starynkevitch
  2015-01-01  0:00                   ` David Malcolm
  2015-01-01  0:00                 ` [PATCH, committed] " David Malcolm
                                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm, gcc-patches, jit

On 06/25/2015 09:13 PM, David Malcolm wrote:
> Some interpreters/VMs support a switch statement (for example the JVM
> has opcodes "lookupswitch" and "tableswitch").  GCC has a set of
> optimizations for efficiently handling switch statements, so it makes
> sense to directly expose switch statements in the libgccjit API.

Yes, I hope that feature will be incorporated quickly in gccjit. I don't 
have the power to approve that patch, but I hope it will be approved.

>
> This patch implements a switch statement, but it isn't quite ready for
> committing to trunk yet:
>
>    * It relies on gcc/typed-splay-tree.h in the previous patch
>
>    * It extends the libgccjit API.  It's not clear to me yet how to
>      manage extensions of the libgccjit API: should I use symbol maps
>      and versioning, or bump the SONAME?  I'm thinking of providing
>      precanned feature macros within libgccjit.h e.g:
>
>        #define LIBGCCJIT_HAVE_SWITCH_STATEMENT
>
>      for the benefit of client code that doesn't use configure
>      scripts.


Perhaps just exposing the major and minor version of the GCC supporting 
that GCCJIT is enough (like it was done for plugins, e.g. 
GCCPLUGIN_VERSION etc)?


Regards.


-- 
Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mine, sont seulement les miennes} ***

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00       ` Dibyendu Majumdar
@ 2015-01-01  0:00         ` David Malcolm
  2015-01-01  0:00           ` Dibyendu Majumdar
  2015-01-01  0:00           ` Basile Starynkevitch
  0 siblings, 2 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Dibyendu Majumdar; +Cc: Basile Starynkevitch, jit

On Tue, 2015-06-23 at 21:01 +0100, Dibyendu Majumdar wrote:
> On 23 June 2015 at 01:25, David Malcolm <dmalcolm@redhat.com> wrote:
> > As it happens, none of the bytecode languages I've implemented so far
> > have switch-like constructs.
> >
> 
> Often bytecodes in dynamic languages when translated do not exactly
> map to C constructs, so this is not surprising I think.
> 
> > But I see now that the JVM has opcodes "lookupswitch" and "tableswitch";
> > those alone make a compelling case for libgccjit supporting switches.
> >
> > I'm working on it now; the API I'm thinking of looks like this:
> >
> > 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_rvalue **case_min_values,
> >                                   gcc_jit_rvalue **case_max_values,
> >                                   gcc_jit_block **case_blocks);
> >
> >
> > thus supporting ranged cases, whilst also allowing individual values, by
> > simply passing in the same table for both case_min_values and
> > case_max_values.
> >

(fwiw I have it mostly working)

> The API looks ok to me but would it be more user friendly if you
> created a struct to hold the case values and blocks together? That is:
> 
> struct case {
>   gcc_jit_rvalue *min_value;
>   gcc_jit_rvalue *max_value;
>   gcc_jit_block *block;
> };
> 
> Then the last three parameters could be replaced by 'struct case *'.

Is is actually easier that way?

If so, it would probably need to be "struct gcc_jit_case", both for the
sake of namespacing, and because "case" is a reserved word.

That said, it would be the first non-opaque struct in the API, which
makes me nervous; I guess we could make it opaque like this:

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 *block);

though I don't know if that's better.

I prefer the original API, fwiw (with 3 arrays), as it doesn't introduce
a new type.

> >> BTW, if we don't have switches, we should at least have indirect jumps,
> >> and the ability to retrieve, as a label, the starting address of any
> >> basic block. (i.e. the equivalent of goto *ptr; and of &&label in C). If
> >> that is possible today, we need more documentation about that (at least
> >> saying that switch statements could be translated that way)
> >>
> 
> It would also be useful to have the indirect jump capability - LLVM
> offers this and I used it in Ravi to help with performance (although
> in the particular use case I did not really see much benefit).

This isn't yet supported by libgccjit.  Maybe the API might look
something like this:

extern gcc_jit_rvalue *
gcc_jit_block_get_address (gcc_jit_block *block);

giving an rvalue of type void *, and:

extern void
gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
                                      gcc_jit_rvalue *expr);

where "expr" must be of type void *, obtained via
gcc_jit_block_get_address.

That said, what use-cases are you thinking of?   The one I'm familiar
with is threaded dispatch of the big switch statement typically seen in
an interpreter, but presumably anyone using libgccjit is unrolling the
switch statement into code.

Dave

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00     ` David Malcolm
@ 2015-01-01  0:00       ` Dibyendu Majumdar
  2015-01-01  0:00         ` David Malcolm
  2015-01-01  0:00       ` switches in GCC JIT? Basile Starynkevitch
  2015-01-01  0:00       ` David Malcolm
  2 siblings, 1 reply; 23+ messages in thread
From: Dibyendu Majumdar @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: Basile Starynkevitch, jit

On 23 June 2015 at 01:25, David Malcolm <dmalcolm@redhat.com> wrote:
> As it happens, none of the bytecode languages I've implemented so far
> have switch-like constructs.
>

Often bytecodes in dynamic languages when translated do not exactly
map to C constructs, so this is not surprising I think.

> But I see now that the JVM has opcodes "lookupswitch" and "tableswitch";
> those alone make a compelling case for libgccjit supporting switches.
>
> I'm working on it now; the API I'm thinking of looks like this:
>
> 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_rvalue **case_min_values,
>                                   gcc_jit_rvalue **case_max_values,
>                                   gcc_jit_block **case_blocks);
>
>
> thus supporting ranged cases, whilst also allowing individual values, by
> simply passing in the same table for both case_min_values and
> case_max_values.
>

The API looks ok to me but would it be more user friendly if you
created a struct to hold the case values and blocks together? That is:

struct case {
  gcc_jit_rvalue *min_value;
  gcc_jit_rvalue *max_value;
  gcc_jit_block *block;
};

Then the last three parameters could be replaced by 'struct case *'.

>> BTW, if we don't have switches, we should at least have indirect jumps,
>> and the ability to retrieve, as a label, the starting address of any
>> basic block. (i.e. the equivalent of goto *ptr; and of &&label in C). If
>> that is possible today, we need more documentation about that (at least
>> saying that switch statements could be translated that way)
>>

It would also be useful to have the indirect jump capability - LLVM
offers this and I used it in Ravi to help with performance (although
in the particular use case I did not really see much benefit).

Regards
Dibyendu

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00                 ` Basile Starynkevitch
@ 2015-01-01  0:00                   ` David Malcolm
  0 siblings, 0 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Basile Starynkevitch; +Cc: gcc-patches, jit

On Thu, 2015-06-25 at 21:47 +0200, Basile Starynkevitch wrote:
> On 06/25/2015 09:13 PM, David Malcolm wrote:
> > Some interpreters/VMs support a switch statement (for example the JVM
> > has opcodes "lookupswitch" and "tableswitch").  GCC has a set of
> > optimizations for efficiently handling switch statements, so it makes
> > sense to directly expose switch statements in the libgccjit API.
> 
> Yes, I hope that feature will be incorporated quickly in gccjit. I don't 
> have the power to approve that patch, but I hope it will be approved.
> > This patch implements a switch statement, but it isn't quite ready for
> > committing to trunk yet:
> >
> >    * It relies on gcc/typed-splay-tree.h in the previous patch
> >
> >    * It extends the libgccjit API.  It's not clear to me yet how to
> >      manage extensions of the libgccjit API: should I use symbol maps
> >      and versioning, or bump the SONAME?  I'm thinking of providing
> >      precanned feature macros within libgccjit.h e.g:
> >
> >        #define LIBGCCJIT_HAVE_SWITCH_STATEMENT
> >
> >      for the benefit of client code that doesn't use configure
> >      scripts.
> 
> 
> Perhaps just exposing the major and minor version of the GCC supporting 
> that GCCJIT is enough (like it was done for plugins, e.g. 
> GCCPLUGIN_VERSION etc)?

That approach rapidly gets messy if we backport anything; doing it by
feature seems saner.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Managing ABI compatibility (was Re: [PATCH 2/2] jit: add switch statements)
  2015-01-01  0:00                   ` Managing ABI compatibility (was Re: [PATCH 2/2] jit: add switch statements) David Malcolm
@ 2015-01-01  0:00                     ` David Malcolm
  0 siblings, 0 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches, jit

On Fri, 2015-06-26 at 13:57 -0400, David Malcolm wrote:
> On Thu, 2015-06-25 at 13:16 -0600, Jeff Law wrote:
> > On 06/25/2015 01:13 PM, David Malcolm wrote:
> > >
> > >    * It extends the libgccjit API.  It's not clear to me yet how to
> > >      manage extensions of the libgccjit API: should I use symbol maps
> > >      and versioning, or bump the SONAME?  I'm thinking of providing
> > >      precanned feature macros within libgccjit.h e.g:
> > >
> > >        #define LIBGCCJIT_HAVE_SWITCH_STATEMENT
> > Seems to me you should use a symbol map and bump the version.
> 
> Thanks.  By "the version", do you mean the version within the symbol
> map?
> 
> I've read Uli Drepper's "How To Write Shared Libraries" guide:
>   http://www.akkadia.org/drepper/dsohowto.pdf
> and in particular section 3: "Maintaining APIs and ABIs", which suggests
> versioned DSOs (though this presumably implies the use of GNU ld).
> 
> I attempted to use the symbol map to segregate the new symbols into
> their own version; attached is a patch which does that (on top of the
> "add switch statements" patch).
> 
> Is that the kind of thing you had in mind?
> 
> My first attempt was to keep the existing syms in an anonymous tag but I
> got:
>   /usr/bin/ld: anonymous version tag cannot be combined with other
> version tags
> (which comes from binutils ld/ldlang.c:lang_register_vers_node)
> 
> Hence I put the existing symbols into a LIBGCCJIT_ABI_1 version.
> 
> With the above change, using "eu-readelf -s" I see a client program
> linked against libgccjit goes from having e.g.:
> 
> GLOBAL DEFAULT    UNDEF gcc_jit_context_compile
> 
> to having:
> 
> GLOBAL DEFAULT    UNDEF gcc_jit_context_compile@@LIBGCCJIT_ABI_1
> 
> and one using the new symbols has:
> 
> GLOBAL DEFAULT    UNDEF gcc_jit_context_compile@LIBGCCJIT_ABI_1 (2)
> GLOBAL DEFAULT    UNDEF gcc_jit_block_end_with_switch@LIBGCCJIT_ABI_2 (4)
> 
> 
> Presumably I'd need to bump the SONAME to accommodate the introduction
> of using symbol versioning?  Am I right in hoping that with the
> introduction to using symbol versioning that I'd never need to bump the
> SONAME again?
> 
> Running:
> 
>   objdump -p client-binary-using-just-old-symbols
> 
> gives (amongst other output):
> 
>   Version References:
>     required from libc.so.6:
>       0x09691a75 0x00 03 GLIBC_2.2.5
>     required from libgccjit.so.0:
>       0x00824161 0x00 02 LIBGCCJIT_ABI_1
> 
> whereas:
> 
>   objdump -p client-binary-using-some-new-symbols
> 
> gives:
> 
>   Version References:
>     required from libc.so.6:
>       0x09691a75 0x00 03 GLIBC_2.2.5
>     required from libgccjit.so.0:
>       0x00824162 0x00 04 LIBGCCJIT_ABI_2
>       0x00824161 0x00 02 LIBGCCJIT_ABI_1
> 
> FWIW objdump -p is used by /usr/lib/rpm/find-requires, so presumably
> these entries would show up in rpm metadata, so that binaries using the
> new symbols get flagged at the RPM level as needing a more up-to-date
> libgccjit rpm.
> 
> Hopefully other packaging systems handle symbol versioning in similar
> ways.
> 
> Does this approach look sane?
> 
> I'd probably add a page about ABI to the docs, and list the ABIs there.
> 
> This doesn't cover the addition of new values to the various options
> enums; I'm not sure how to handle capturing *that* within the binary
> metadata without bumping the SONAME.  Maybe bump the
> gcc_jit_context_set_FOO_option entrypoints into new version tags each
> time we add a new value to the corresponding option enum?

Maybe having public enums for the options was a mistake; perhaps
additional options should get their own API entrypoints:

e.g.
  gcc_jit_context_set_bool_option_name_of_option
  gcc_jit_context_set_int_option_name_of_option
  gcc_jit_context_set_str_option_name_of_option

so perhaps:

  extern void
  gcc_jit_context_set_bool_option_allow_unreachable_blocks (gcc_jit_context *ctxt,
	                                                     int value);

Quite a mouthful, but it means we can precisely identify client binaries
using it via metadata.  (perhaps lose the "bool_", "option_" or
bool_option_" part?)

[...snip...]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00             ` [PATCH 1/2] Add gcc/typed-splay-tree.h David Malcolm
@ 2015-01-01  0:00               ` David Malcolm
  2015-01-01  0:00                 ` Basile Starynkevitch
                                   ` (3 more replies)
  2015-01-01  0:00               ` [PATCH 1/2] Add gcc/typed-splay-tree.h Jeff Law
  1 sibling, 4 replies; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: gcc-patches, jit; +Cc: David Malcolm

Some interpreters/VMs support a switch statement (for example the JVM
has opcodes "lookupswitch" and "tableswitch").  GCC has a set of
optimizations for efficiently handling switch statements, so it makes
sense to directly expose switch statements in the libgccjit API.

This patch implements a switch statement, but it isn't quite ready for
committing to trunk yet:

  * It relies on gcc/typed-splay-tree.h in the previous patch

  * It extends the libgccjit API.  It's not clear to me yet how to
    manage extensions of the libgccjit API: should I use symbol maps
    and versioning, or bump the SONAME?  I'm thinking of providing
    precanned feature macros within libgccjit.h e.g:

      #define LIBGCCJIT_HAVE_SWITCH_STATEMENT

    for the benefit of client code that doesn't use configure
    scripts.

  * It touches the jit docs and thus needs a refresh of the generated
    texinfo.

The initial implementation I tried was:

  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_rvalue **case_min_values,
                                 gcc_jit_rvalue **case_max_values,
                                 gcc_jit_block **case_blocks);

but I've now implemented it with an explicit "gcc_jit_case" type:

  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);

  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);

The motivation for this was:

  * error-checking became easier (esp. for overlapping ranges etc)
  * making debug strings for the switch statement became easier
  * probably nicer from a language-binding POV

The requirement to allow more than 2 successor blocks for a gcc_jit_block
required updating the internal get_successor_blocks API for blocks and
block-terminating statements from:

  int
  get_successor_blocks (block **next1, block **next2) const;

to:

  vec <recording::block *>
  get_successor_blocks () const;

where ownership of the vector transfers to the caller, which
must call its release () method.

The patch extends the C and C++ APIs, adding documentation and
testcases.

I've also extended the Python bindings to cover the new entrypoints;
this can be seen in the "switch" branch of pygccjit here:
https://github.com/davidmalcolm/pygccjit/tree/switch

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/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.
	* 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::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.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.
---
 gcc/jit/docs/cp/topics/functions.rst               |  69 +++-
 gcc/jit/docs/cp/topics/objects.rst                 |   1 +
 gcc/jit/docs/topics/functions.rst                  |  81 ++++-
 gcc/jit/docs/topics/objects.rst                    |   1 +
 gcc/jit/jit-common.h                               |   2 +
 gcc/jit/jit-playback.c                             |  72 ++++
 gcc/jit/jit-playback.h                             |  19 +
 gcc/jit/jit-recording.c                            | 322 ++++++++++++++---
 gcc/jit/jit-recording.h                            |  87 ++++-
 gcc/jit/libgccjit++.h                              |  63 ++++
 gcc/jit/libgccjit.c                                | 381 +++++++++++++++++++++
 gcc/jit/libgccjit.h                                |  61 ++++
 gcc/jit/libgccjit.map                              |   3 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h       |  10 +
 ...error-gcc_jit_block_end_with_switch-NULL-case.c |  66 ++++
 ...t_block_end_with_switch-mismatching-case-type.c |  83 +++++
 ..._jit_block_end_with_switch-overlapping-ranges.c |  95 +++++
 ...rror-gcc_jit_context_new_case-non-const-label.c |  80 +++++
 ...ror-gcc_jit_context_new_case-non-integer-type.c |  81 +++++
 ...r-gcc_jit_context_new_case-reversed-endpoints.c |  80 +++++
 gcc/testsuite/jit.dg/test-switch.c                 | 147 ++++++++
 gcc/testsuite/jit.dg/test-switch.cc                | 118 +++++++
 22 files changed, 1861 insertions(+), 61 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
 create mode 100644 gcc/testsuite/jit.dg/test-switch.c
 create mode 100644 gcc/testsuite/jit.dg/test-switch.cc

diff --git a/gcc/jit/docs/cp/topics/functions.rst b/gcc/jit/docs/cp/topics/functions.rst
index de3570a..801cdbe 100644
--- a/gcc/jit/docs/cp/topics/functions.rst
+++ b/gcc/jit/docs/cp/topics/functions.rst
@@ -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,69 @@ 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).
+
+   .. 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++
diff --git a/gcc/jit/docs/cp/topics/objects.rst b/gcc/jit/docs/cp/topics/objects.rst
index 714b645..8d99bd4 100644
--- a/gcc/jit/docs/cp/topics/objects.rst
+++ b/gcc/jit/docs/cp/topics/objects.rst
@@ -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:
 
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index 35e58d3..a4f72bb 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -161,7 +161,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.
@@ -342,3 +343,81 @@ 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).
+
+   .. 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
diff --git a/gcc/jit/docs/topics/objects.rst b/gcc/jit/docs/topics/objects.rst
index 3ae6309..85c783c 100644
--- a/gcc/jit/docs/topics/objects.rst
+++ b/gcc/jit/docs/topics/objects.rst
@@ -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`:
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 5a8a56d..4052052 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -130,6 +130,7 @@ namespace recording {
 	class global;
         class param;
     class statement;
+    class case_;
 
   /* End of recording types. */
 }
@@ -151,6 +152,7 @@ namespace playback {
     class source_file;
     class source_line;
     class location;
+    class case_;
 
   /* End of playback types. */
 }
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index ee492ef..aff52c8 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -1578,6 +1578,78 @@ 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_case_label =
+    build_case_label (t_low_value, t_high_value,
+		      create_artificial_label (UNKNOWN_LOCATION));
+  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::
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index deb84ef..73ab1ca 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -440,6 +440,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:
@@ -481,6 +494,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)
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index 09bb738..6dbd009 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -1086,6 +1086,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.
 
@@ -3553,23 +3569,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.  */
@@ -3818,6 +3824,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
@@ -3895,24 +3925,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
@@ -3990,12 +4016,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.  */
@@ -4140,6 +4168,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 <>
@@ -4172,6 +4210,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 <>
@@ -4225,6 +4273,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 <>
@@ -4264,6 +4321,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. */
 
@@ -5262,14 +5328,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
@@ -5478,13 +5545,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
@@ -5542,12 +5610,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
@@ -5593,11 +5662,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
@@ -5635,6 +5705,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
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 090eb5d..4dd2249 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -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);
@@ -961,6 +966,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;
 
@@ -1159,6 +1167,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
   {
@@ -1174,7 +1189,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 ();
@@ -1240,6 +1255,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);
@@ -1603,8 +1622,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);
 
@@ -1728,8 +1746,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 ();
@@ -1752,8 +1769,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 ();
@@ -1774,8 +1790,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 ();
@@ -1785,6 +1800,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
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 62ef6a4..40e123b 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -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
@@ -293,6 +294,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;
   };
@@ -417,6 +422,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
@@ -467,6 +476,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).
@@ -1108,6 +1125,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
@@ -1355,6 +1383,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)
@@ -1545,6 +1594,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)
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 6e57127..cad1579 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "opts.h"
 #include "safe-ctype.h"
+#include "typed-splay-tree.h"
 
 #include "libgccjit.h"
 #include "jit-common.h"
@@ -84,6 +85,10 @@ struct gcc_jit_param : public gcc::jit::recording::param
 {
 };
 
+struct gcc_jit_case : public gcc::jit::recording::case_
+{
+};
+
 /**********************************************************************
  Error-handling.
 
@@ -2123,6 +2128,382 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block,
   block->end_with_return (loc, NULL);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::new_case method in
+   jit-recording.c.  */
+
+gcc_jit_case *
+gcc_jit_context_new_case (gcc_jit_context *ctxt,
+			  gcc_jit_rvalue *min_value,
+			  gcc_jit_rvalue *max_value,
+			  gcc_jit_block *block)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  RETURN_NULL_IF_FAIL (min_value, ctxt, NULL, "NULL min_value");
+  RETURN_NULL_IF_FAIL (max_value, ctxt, NULL, "NULL max_value");
+  RETURN_NULL_IF_FAIL (block, ctxt, NULL, "NULL block");
+
+  RETURN_NULL_IF_FAIL_PRINTF1 (min_value->is_constant (), ctxt, NULL,
+			       "min_value is not a constant: %s",
+			       min_value->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF1 (max_value->is_constant (), ctxt, NULL,
+			       "max_value is not a constant: %s",
+			       max_value->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    min_value->get_type ()->is_int (),
+    ctxt, NULL,
+    "min_value: %s (type: %s) is not of integer type",
+    min_value->get_debug_string (),
+    min_value->get_type ()->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    max_value->get_type ()->is_int (),
+    ctxt, NULL,
+    "max_value: %s (type: %s) is not of integer type",
+    max_value->get_debug_string (),
+    max_value->get_type ()->get_debug_string ());
+
+  wide_int wi_min, wi_max;
+  if (!min_value->get_wide_int (&wi_min))
+    gcc_unreachable ();
+  if (!max_value->get_wide_int (&wi_max))
+    gcc_unreachable ();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    wi::les_p (wi_min, wi_max),
+    ctxt, NULL,
+    "min_value: %s > max_value: %s",
+    min_value->get_debug_string (),
+    max_value->get_debug_string ());
+  return (gcc_jit_case *)ctxt->new_case (min_value,
+					 max_value,
+					 block);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, this calls the trivial
+   gcc::jit::recording::memento::as_object method (a case is a
+   memento), in jit-recording.h.  */
+
+gcc_jit_object *
+gcc_jit_case_as_object (gcc_jit_case *case_)
+{
+  RETURN_NULL_IF_FAIL (case_, NULL, NULL, "NULL case");
+
+  return static_cast <gcc_jit_object *> (case_->as_object ());
+}
+
+/* Helper function for gcc_jit_block_end_with_switch and
+   valid_case_for_switch.  */
+
+static bool
+valid_dest_for_switch (gcc::jit::recording::context *ctxt,
+		       gcc_jit_location *loc,
+		       const char *api_funcname,
+		       gcc::jit::recording::block *switch_block,
+		       gcc::jit::recording::block *dest_block,
+		       const char *dest_block_desc)
+{
+  if (!dest_block)
+    {
+      jit_error (ctxt, loc, "%s: NULL %s", api_funcname, dest_block_desc);
+      return false;
+    }
+  gcc::jit::recording::function *switch_fn = switch_block->get_function ();
+  gcc::jit::recording::function *dest_fn = dest_block->get_function ();
+  if (switch_fn != dest_fn)
+    {
+      jit_error (ctxt, loc,
+		 "%s: %s is not in same function:"
+		 " switch block %s is in function %s"
+		 " whereas %s %s is in function %s",
+		 api_funcname,
+		 dest_block_desc,
+		 switch_block->get_debug_string (),
+		 switch_fn->get_debug_string (),
+		 dest_block_desc,
+		 dest_block->get_debug_string (),
+		 dest_fn->get_debug_string ());
+      return false;
+    }
+  return true;
+}
+
+/* Helper function for gcc_jit_block_end_with_switch.  */
+
+static bool
+valid_case_for_switch (gcc::jit::recording::context *ctxt,
+		       gcc_jit_location *loc,
+		       const char *api_funcname,
+		       gcc_jit_block *switch_block,
+		       gcc_jit_rvalue *expr,
+		       gcc_jit_case *case_,
+		       const char *case_desc,
+		       int case_idx)
+{
+  if (!case_)
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " NULL case %i",
+		 api_funcname,
+		 case_idx);
+      return false;
+    }
+  if (!valid_dest_for_switch (ctxt, loc,
+			      api_funcname,
+			      switch_block,
+			      case_->get_dest_block (),
+			      case_desc))
+    return false;
+  gcc::jit::recording::type *expr_type = expr->get_type ();
+  if (expr_type != case_->get_min_value ()->get_type ())
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " mismatching types between case and expression:"
+		 " cases[%i]->min_value: %s (type: %s)"
+		 " expr: %s (type: %s)",
+		 api_funcname,
+		 case_idx,
+		 case_->get_min_value ()->get_debug_string (),
+		 case_->get_min_value ()->get_type ()->get_debug_string (),
+		 expr->get_debug_string (),
+		 expr_type->get_debug_string ());
+      return false;
+    }
+  if (expr_type != case_->get_max_value ()->get_type ())
+    {
+      jit_error (ctxt, loc,
+		 "%s:"
+		 " mismatching types between case and expression:"
+		 " cases[%i]->max_value: %s (type: %s)"
+		 " expr: %s (type: %s)",
+		 api_funcname,
+		 case_idx,
+		 case_->get_max_value ()->get_debug_string (),
+		 case_->get_max_value ()->get_type ()->get_debug_string (),
+		 expr->get_debug_string (),
+		 expr_type->get_debug_string ());
+      return false;
+    }
+  return true;
+}
+
+/* A class for holding the data we need to perform error-checking
+   on a libgccjit API call.  */
+
+class api_call_validator
+{
+ public:
+  api_call_validator (gcc::jit::recording::context *ctxt,
+		      gcc_jit_location *loc,
+		      const char *funcname)
+  : m_ctxt (ctxt),
+    m_loc (loc),
+    m_funcname (funcname)
+  {}
+
+ protected:
+  gcc::jit::recording::context *m_ctxt;
+  gcc_jit_location *m_loc;
+  const char *m_funcname;
+};
+
+/* A class for verifying that the ranges of cases within
+   gcc_jit_block_end_with_switch don't overlap.  */
+
+class case_range_validator : public api_call_validator
+{
+ public:
+  case_range_validator (gcc::jit::recording::context *ctxt,
+			gcc_jit_location *loc,
+			const char *funcname);
+
+  bool
+  validate (gcc_jit_case *case_, int idx);
+
+ private:
+  static int
+  case_compare (gcc::jit::recording::rvalue *k1,
+		gcc::jit::recording::rvalue *k2);
+
+  static wide_int
+  get_wide_int (gcc::jit::recording::rvalue *k);
+
+ private:
+  typed_splay_tree <gcc::jit::recording::rvalue *, gcc_jit_case *> m_cases;
+};
+
+/* case_range_validator's ctor.  */
+
+case_range_validator::case_range_validator (gcc::jit::recording::context *ctxt,
+					    gcc_jit_location *loc,
+					    const char *funcname)
+: api_call_validator (ctxt, loc, funcname),
+  m_cases (case_compare, NULL, NULL)
+{
+}
+
+/* Ensure that the range of CASE_ does not overlap with any of the
+   ranges of cases we've already seen.
+   Return true if everything is OK.
+   Return false and emit an error if there is an overlap.
+   Compare with c-family/c-common.c:c_add_case_label.  */
+
+bool
+case_range_validator::validate (gcc_jit_case *case_,
+				int case_idx)
+{
+  /* Look up the LOW_VALUE in the table of case labels we already
+     have.  */
+  gcc_jit_case *other = m_cases.lookup (case_->get_min_value ());
+
+  /* If there was not an exact match, check for overlapping ranges.  */
+  if (!other)
+    {
+      gcc_jit_case *pred;
+      gcc_jit_case *succ;
+
+      /* Even though there wasn't an exact match, there might be an
+	 overlap between this case range and another case range.
+	 Since we've (inductively) not allowed any overlapping case
+	 ranges, we simply need to find the greatest low case label
+	 that is smaller that CASE_MIN_VALUE, and the smallest low case
+	 label that is greater than CASE_MAX_VALUE.  If there is an overlap
+	 it will occur in one of these two ranges.  */
+      pred = m_cases.predecessor (case_->get_min_value ());
+      succ = m_cases.successor (case_->get_max_value ());
+
+      /* Check to see if the PRED overlaps.  It is smaller than
+	 the LOW_VALUE, so we only need to check its max value.  */
+      if (pred)
+	{
+	  wide_int wi_case_min = get_wide_int (case_->get_min_value ());
+	  wide_int wi_pred_max = get_wide_int (pred->get_max_value ());
+	  if (wi::ges_p (wi_pred_max, wi_case_min))
+	    other = pred;
+	}
+
+      if (!other && succ)
+	{
+	  /* Check to see if the SUCC overlaps.  The low end of that
+	     range is bigger than the low end of the current range.  */
+	  wide_int wi_case_max = get_wide_int (case_->get_max_value ());
+	  wide_int wi_succ_min = get_wide_int (succ->get_min_value ());
+	  if (wi::les_p (wi_succ_min, wi_case_max))
+	    other = succ;
+	}
+    }
+
+  /* If there was an overlap, issue an error.  */
+  if (other)
+    {
+      jit_error (m_ctxt, m_loc,
+		 "%s: duplicate (or overlapping) cases values:"
+		 " case %i: %s overlaps %s",
+		 m_funcname,
+		 case_idx,
+		 case_->get_debug_string (),
+		 other->get_debug_string ());
+      return false;
+    }
+
+  /* Register this case label in the splay tree.  */
+  m_cases.insert (case_->get_min_value (),
+		  case_);
+  return true;
+}
+
+/* Compare with c-family/c-common.c:case_compare, which acts on tree
+   nodes, rather than rvalue *.
+
+   Comparator for case label values.  K1 and K2 must be constant integer
+   values (anything else should have been rejected by
+   gcc_jit_context_new_case.
+
+   Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after
+   K2, and 0 if K1 and K2 are equal.  */
+
+int
+case_range_validator::case_compare (gcc::jit::recording::rvalue * k1,
+				    gcc::jit::recording::rvalue * k2)
+{
+  wide_int wi1 = get_wide_int (k1);
+  wide_int wi2 = get_wide_int (k2);
+  return wi::cmps(wi1, wi2);
+}
+
+/* Given a const int rvalue K, get the underlying value as a wide_int.  */
+
+wide_int
+case_range_validator::get_wide_int (gcc::jit::recording::rvalue *k)
+{
+  wide_int wi;
+  bool got_wi = k->get_wide_int (&wi);
+  gcc_assert (got_wi);
+  return wi;
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::end_with_switch method in
+   jit-recording.c.  */
+
+void
+gcc_jit_block_end_with_switch (gcc_jit_block *block,
+			       gcc_jit_location *loc,
+			       gcc_jit_rvalue *expr,
+			       gcc_jit_block *default_block,
+			       int num_cases,
+			       gcc_jit_case **cases)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_IF_FAIL (expr, ctxt, loc,
+		  "NULL expr");
+  gcc::jit::recording::type *expr_type = expr->get_type ();
+  RETURN_IF_FAIL_PRINTF2 (
+    expr_type->is_int (),
+    ctxt, loc,
+    "expr: %s (type: %s) is not of integer type",
+    expr->get_debug_string (),
+    expr_type->get_debug_string ());
+  if (!valid_dest_for_switch (ctxt, loc,
+			      __func__,
+			      block,
+			      default_block,
+			      "default_block"))
+    return;
+  RETURN_IF_FAIL (num_cases >= 0, ctxt, loc, "num_cases < 0");
+  case_range_validator crv (ctxt, loc, __func__);
+  for (int i = 0; i < num_cases; i++)
+    {
+      char case_desc[32];
+      snprintf (case_desc, sizeof (case_desc),
+		"cases[%i]", i);
+      if (!valid_case_for_switch (ctxt, loc,
+				  __func__,
+				  block,
+				  expr,
+				  cases[i],
+				  case_desc,
+				  i))
+	return;
+      if (!crv.validate (cases[i], i))
+	return;
+    }
+
+  block->end_with_switch (loc, expr, default_block,
+			  num_cases,
+			  (gcc::jit::recording::case_ **)cases);
+}
+
 /**********************************************************************
  Option-management
  **********************************************************************/
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 12d3f29..a6874f4 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -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);
@@ -1076,6 +1083,60 @@ 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.  */
+
+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.  */
+
+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).  */
+
+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);
+
 /**********************************************************************
  Nested contexts.
  **********************************************************************/
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 2fa6217..93b19e8 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -29,7 +29,9 @@
     gcc_jit_block_end_with_jump;
     gcc_jit_block_end_with_return;
     gcc_jit_block_end_with_void_return;
+    gcc_jit_block_end_with_switch;
     gcc_jit_block_get_function;
+    gcc_jit_case_as_object;
     gcc_jit_context_acquire;
     gcc_jit_context_add_option;
     gcc_jit_context_compile;
@@ -47,6 +49,7 @@
     gcc_jit_context_new_binary_op;
     gcc_jit_context_new_call;
     gcc_jit_context_new_call_through_ptr;
+    gcc_jit_context_new_case;
     gcc_jit_context_new_cast;
     gcc_jit_context_new_child_context;
     gcc_jit_context_new_comparison;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index a54041f..15fa556 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -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
@@ -277,6 +284,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},
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
new file mode 100644
index 0000000..07a9848
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-NULL-case.c
@@ -0,0 +1,66 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
new file mode 100644
index 0000000..cc907ce
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-mismatching-case-type.c
@@ -0,0 +1,83 @@
+#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)");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
new file mode 100644
index 0000000..40655c2
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_block_end_with_switch-overlapping-ranges.c
@@ -0,0 +1,95 @@
+#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;");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
new file mode 100644
index 0000000..3953818
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-const-label.c
@@ -0,0 +1,80 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
new file mode 100644
index 0000000..5d44286
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-non-integer-type.c
@@ -0,0 +1,81 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
new file mode 100644
index 0000000..a84d9f3
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_context_new_case-reversed-endpoints.c
@@ -0,0 +1,80 @@
+#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");
+}
diff --git a/gcc/testsuite/jit.dg/test-switch.c b/gcc/testsuite/jit.dg/test-switch.c
new file mode 100644
index 0000000..74088c8
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-switch.c
@@ -0,0 +1,147 @@
+#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);
+    }
+}
diff --git a/gcc/testsuite/jit.dg/test-switch.cc b/gcc/testsuite/jit.dg/test-switch.cc
new file mode 100644
index 0000000..862f7a8
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-switch.cc
@@ -0,0 +1,118 @@
+#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);
+    }
+}
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
                                   ` (2 preceding siblings ...)
  2015-01-01  0:00                 ` [PATCH 2/2] " Basile Starynkevitch
@ 2015-01-01  0:00                 ` Jeff Law
  2015-01-01  0:00                   ` Managing ABI compatibility (was Re: [PATCH 2/2] jit: add switch statements) David Malcolm
  3 siblings, 1 reply; 23+ messages in thread
From: Jeff Law @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm, gcc-patches, jit

On 06/25/2015 01:13 PM, David Malcolm wrote:
>
>    * It extends the libgccjit API.  It's not clear to me yet how to
>      manage extensions of the libgccjit API: should I use symbol maps
>      and versioning, or bump the SONAME?  I'm thinking of providing
>      precanned feature macros within libgccjit.h e.g:
>
>        #define LIBGCCJIT_HAVE_SWITCH_STATEMENT
Seems to me you should use a symbol map and bump the version.

jeff
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/2] jit: add switch statements
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
  2015-01-01  0:00                 ` Basile Starynkevitch
  2015-01-01  0:00                 ` [PATCH, committed] " David Malcolm
@ 2015-01-01  0:00                 ` Basile Starynkevitch
  2015-01-01  0:00                   ` David Malcolm
  2015-01-01  0:00                 ` Jeff Law
  3 siblings, 1 reply; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit

On Thu, Jun 25, 2015 at 03:13:41PM -0400, David Malcolm wrote:
> Some interpreters/VMs support a switch statement (for example the JVM
> has opcodes "lookupswitch" and "tableswitch").  GCC has a set of
> optimizations for efficiently handling switch statements, so it makes
> sense to directly expose switch statements in the libgccjit API.
> 
> This patch implements a switch statement, but it isn't quite ready for
> committing to trunk yet:
> 


Is that patch available (thru git or svn) on some branch?
I did not found any *jit* branch on svn://gcc.gnu.org/svn/gcc/branches

Do you believe it will get into GCC 6 (probably yes)?

Do you believe it would get into GCC 5.2 (unfortunately, perhaps no)?

Do you think it could easily be backported into GCC 5.x ?

Cheers!

-- 
Basile Starynkevitch             http://starynkevitch.net/Basile
France

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00     ` David Malcolm
  2015-01-01  0:00       ` Dibyendu Majumdar
@ 2015-01-01  0:00       ` Basile Starynkevitch
  2015-01-01  0:00       ` David Malcolm
  2 siblings, 0 replies; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit

On 06/23/2015 02:25 AM, David Malcolm wrote:

> As it happens, none of the bytecode languages I've implemented so far
> have switch-like constructs.
>
> But I see now that the JVM has opcodes "lookupswitch" and "tableswitch";
> those alone make a compelling case for libgccjit supporting switches.
>
> I'm working on it now; the API I'm thinking of looks like this:
>
> 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_rvalue **case_min_values,
> 			          gcc_jit_rvalue **case_max_values,
> 			          gcc_jit_block **case_blocks);
>
>
> thus supporting ranged cases, whilst also allowing individual values, by
> simply passing in the same table for both case_min_values and
> case_max_values.


Great. Don't forget in the documentation to say that simple cases can be 
be implemented by sharing the case_min_values & case_max_value, or 
perhaps even provide also a static inline function doing that.

A big thanks for being so responsive!

Regards

-- 
Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mine, sont seulement les miennes} ***

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00 ` David Malcolm
@ 2015-01-01  0:00   ` Basile Starynkevitch
  2015-01-01  0:00     ` David Malcolm
  0 siblings, 1 reply; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit

On 06/22/2015 11:42 PM, David Malcolm wrote:
> On Mon, 2015-06-22 at 23:40 +0200, Basile Starynkevitch wrote:
>> Hello David & all
>>
>> I'm guessing that GCCJIT is able to emit switch like statements, e.g.
>> using GIMPLE_SWITCH statements
>> internally.
> No, it doesn't.
>
>> But I don't understand how is it possible. It looks like
>> gimple_build_switch does not occur in gcc/jit/
> Currently gcc/jit builds functions at the tree level and hands them off
> to the gimplifier, so you wouldn't see that in any case.  (in theory it
> could be ported to directly generate gimple).
>
>> Are switch statements omitted from GCCJIT?
> Yes.
>
>> If yes, why???
> I intentionally didn't implement them, to keep the API simpler.
>
> I've never run into a need for them when implementing jit-compilation,
> and in theory they could be implemented using conditionals (albeit
> without the nice optimizations that we have for lowering GIMPLE_SWITCH).
>
> There was some discussion about this here:
>   https://gcc.gnu.org/ml/jit/2014-q4/msg00116.html
>
>> David, do you intend to improve that?
> Do you have a use-case for them?  We can add them if we need them.

Any language (MELT, Ocaml, Haskell, ....) having some pattern matching 
would use a lot of switches.
Or most efficient implementations of Rete algorithm, or similar stuff 
when compiling Prolog-like or CLIPS-like rules.

Also, translation of most finite state automatons is done by a switch 
(often a quite big one, with one case per each state).

At last, any kind of "byte-code" interpreter uses switches.

And many languages have a switch like construct, that would be trivial 
to translate to a GIMPLE_SWITCH, but painful to translate otherwise.


All the bytecodes I know (e.g. JVM & Ocaml) have switch-like constructs, 
and it would be easy to translate them to a GIMPLE_SWITCH, and painful 
otherwise.

BTW, if we don't have switches, we should at least have indirect jumps, 
and the ability to retrieve, as a label, the starting address of any 
basic block. (i.e. the equivalent of goto *ptr; and of &&label in C). If 
that is possible today, we need more documentation about that (at least 
saying that switch statements could be translated that way)

And of course, leveraging on all the important optimizations done by GCC 
on GIMPLE_SWITCH is essential...

Actually, I'm surprised you are asking what is the use case for 
switches. I feel they are obvious and numerous... I would be annoyed, 
e.g. if I could not use switches in my C++ or C code. Replacing a switch 
with a sequence of if is an annoyance, and is probably a major 
performance loss (unless GCC optimizations are clever enough to replace 
them with a switch; which might sometimes be true, but not always).

Regards

-- 
Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mine, sont seulement les miennes} ***

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00         ` David Malcolm
  2015-01-01  0:00           ` Dibyendu Majumdar
@ 2015-01-01  0:00           ` Basile Starynkevitch
  2015-01-01  0:00             ` [PATCH 1/2] Add gcc/typed-splay-tree.h David Malcolm
  1 sibling, 1 reply; 23+ messages in thread
From: Basile Starynkevitch @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm, Dibyendu Majumdar; +Cc: jit


It would also be useful to have the indirect jump capability - LLVM
offers this and I used it in Ravi to help with performance (although
in the particular use case I did not really see much benefit).

> If so, it would probably need to be "struct gcc_jit_case", both for the
> sake of namespacing, and because "case" is a reserved word.
>
> That said, it would be the first non-opaque struct in the API, which
> makes me nervous; I guess we could make it opaque like this:
>
> 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 *block);

Then probably gcc_jit_case would be an "object" and released with the 
context as are all others.
> though I don't know if that's better.
>
> I prefer the original API, fwiw (with 3 arrays), as it doesn't introduce
> a new type.

I'm ok with both, but the API with 3 arrays should IMHO also have a 
simpler function for non-range cases only.

> This isn't yet supported by libgccjit.  Maybe the API might look
> something like this:
>
> extern gcc_jit_rvalue *
> gcc_jit_block_get_address (gcc_jit_block *block);
>
> giving an rvalue of type void *, and:
>
> extern void
> gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
>                                        gcc_jit_rvalue *expr);
>
> where "expr" must be of type void *, obtained via
> gcc_jit_block_get_address.
>
> That said, what use-cases are you thinking of?   The one I'm familiar
> with is threaded dispatch of the big switch statement typically seen in
> an interpreter, but presumably anyone using libgccjit is unrolling the
> switch statement into code.

Not always.

First, a possible use case for gccjit might be to compete with LLVM, 
that is to use gccjit (either as an ahead-of-time compiler component or 
as a JIT compiler component) as a framework for implementing any kind of 
compiler. Then you surely want most GCC and GIMPLE features to be 
available in GCCJIT.

(To be more precise, the OpenMP gimple codes are indeed not very useful 
in GCCJIT - or at least I believe they are not a priority; but the 
exception framework in Gimple, which today is not yet available in 
GCCJIT, might be useful to some)

Then, there are also realistic use cases for indirect jump inside a JIT. 
I would guess that an IPTABLE reimplementation which would JIT the 
iptables thing might want it. And every language which has an indirect 
jump would also profit from it.

Actually, my biased opinion on order of implementation of new features 
in GCCJIT is:

1. most important and most urgent: the switch & case support, without 
which many people (me included) won't use GCCJIT, because switch like 
statements are so common (and very useful) in a lot of languages
(and they are not the same as a sequence of if -else if...); BTW a naive 
implementation of some Javscript JIT infrastructure would also need it.

2. computed gotos & labels

3. exception support.

We should be able to handle the "dynamic Pascal" example of libjit on 
https://www.gnu.org/software/libjit/doc/libjit_3.html#Dynamic-Pascal as 
easily in gccjit as it is possible in libjit.


Is there any support for tailcails in GCCJIT? I guess that any call 
which GCC would optimize to a tailcall would also be a tailcal in GCCJIT....

Regards.

-- 
Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mine, sont seulement les miennes} ***

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: switches in GCC JIT?
  2015-01-01  0:00 switches in GCC JIT? Basile Starynkevitch
@ 2015-01-01  0:00 ` David Malcolm
  2015-01-01  0:00   ` Basile Starynkevitch
  0 siblings, 1 reply; 23+ messages in thread
From: David Malcolm @ 2015-01-01  0:00 UTC (permalink / raw)
  To: Basile Starynkevitch; +Cc: jit

On Mon, 2015-06-22 at 23:40 +0200, Basile Starynkevitch wrote:
> Hello David & all
> 
> I'm guessing that GCCJIT is able to emit switch like statements, e.g. 
> using GIMPLE_SWITCH statements
> internally.

No, it doesn't.

> But I don't understand how is it possible. It looks like 
> gimple_build_switch does not occur in gcc/jit/

Currently gcc/jit builds functions at the tree level and hands them off
to the gimplifier, so you wouldn't see that in any case.  (in theory it
could be ported to directly generate gimple).

> Are switch statements omitted from GCCJIT? 

Yes.

> If yes, why???

I intentionally didn't implement them, to keep the API simpler.

I've never run into a need for them when implementing jit-compilation,
and in theory they could be implemented using conditionals (albeit
without the nice optimizations that we have for lowering GIMPLE_SWITCH).

There was some discussion about this here:
 https://gcc.gnu.org/ml/jit/2014-q4/msg00116.html

> David, do you intend to improve that?

Do you have a use-case for them?  We can add them if we need them.

Dave

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/2] Add gcc/typed-splay-tree.h
  2015-01-01  0:00             ` [PATCH 1/2] Add gcc/typed-splay-tree.h David Malcolm
  2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
@ 2015-01-01  0:00               ` Jeff Law
  2015-01-01  0:00                 ` David Malcolm
  1 sibling, 1 reply; 23+ messages in thread
From: Jeff Law @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm, gcc-patches, jit

On 06/25/2015 01:13 PM, David Malcolm wrote:
> I found when implementing switch statements for the jit that it
> was much easier to work with libiberty's splay-tree.h by first
> wrapping it in a C++ wrapper to add typesafety.
>
> This patch adds such a wrapper, implementing the methods I needed.
>
> It's unused in this patch, but is used in the followup patch (which only
> touches the jit).
>
> OK for trunk?
>
> gcc/ChangeLog:
> 	* typed-splay-tree.h: New file.
OK.
jeff

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/2] Add gcc/typed-splay-tree.h
  2015-01-01  0:00                 ` David Malcolm
@ 2015-01-01  0:00                   ` Jeff Law
  0 siblings, 0 replies; 23+ messages in thread
From: Jeff Law @ 2015-01-01  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches, jit

On 06/29/2015 11:36 AM, David Malcolm wrote:
> On Thu, 2015-06-25 at 13:18 -0600, Jeff Law wrote:
>> On 06/25/2015 01:13 PM, David Malcolm wrote:
>>> I found when implementing switch statements for the jit that it
>>> was much easier to work with libiberty's splay-tree.h by first
>>> wrapping it in a C++ wrapper to add typesafety.
>>>
>>> This patch adds such a wrapper, implementing the methods I needed.
>>>
>>> It's unused in this patch, but is used in the followup patch (which only
>>> touches the jit).
>>>
>>> OK for trunk?
>>>
>>> gcc/ChangeLog:
>>> 	* typed-splay-tree.h: New file.
>> OK.
>
> Well, this is embarrassing, it seems the patch I posted to the list
> doesn't actually compile.
It happens.  It's one of the reasons why we have the rules around 
bootstrap testing :-)  I can't count how many of these kinds of goofs 
I've made through the years.


>
> The underlying splay_tree_insert returns a splay_tree_node, I had the
> typed_splay_tree::insert returning a value_type.  I dropped this bogus
> return type from the "insert" method in the implementation, in favor of
> "void", but I forgot to update the declaration, leading to errors when
> attempting to actually compile this (via jit/jit-recording.c in the
> followup patch).
>
> The attached one-liner patch drops it from the declaration, and applies
> to [PATCH 1/2].  I don't know if I can count this as "obvious"...  It
> does compile now, and "make check-jit" looks good.
I'd think this qualifies :-)

jeff

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2015-07-01 17:35 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-01  0:00 switches in GCC JIT? Basile Starynkevitch
2015-01-01  0:00 ` David Malcolm
2015-01-01  0:00   ` Basile Starynkevitch
2015-01-01  0:00     ` David Malcolm
2015-01-01  0:00       ` Dibyendu Majumdar
2015-01-01  0:00         ` David Malcolm
2015-01-01  0:00           ` Dibyendu Majumdar
2015-01-01  0:00           ` Basile Starynkevitch
2015-01-01  0:00             ` [PATCH 1/2] Add gcc/typed-splay-tree.h David Malcolm
2015-01-01  0:00               ` [PATCH 2/2] jit: add switch statements David Malcolm
2015-01-01  0:00                 ` Basile Starynkevitch
2015-01-01  0:00                   ` David Malcolm
2015-01-01  0:00                 ` [PATCH, committed] " David Malcolm
2015-01-01  0:00                 ` [PATCH 2/2] " Basile Starynkevitch
2015-01-01  0:00                   ` David Malcolm
2015-01-01  0:00                 ` Jeff Law
2015-01-01  0:00                   ` Managing ABI compatibility (was Re: [PATCH 2/2] jit: add switch statements) David Malcolm
2015-01-01  0:00                     ` David Malcolm
2015-01-01  0:00               ` [PATCH 1/2] Add gcc/typed-splay-tree.h Jeff Law
2015-01-01  0:00                 ` David Malcolm
2015-01-01  0:00                   ` Jeff Law
2015-01-01  0:00       ` switches in GCC JIT? Basile Starynkevitch
2015-01-01  0:00       ` David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).