Skip to content

Commit d0bd330

Browse files
committed
Draft "Unreachable Switch Statement Default Cases"
1 parent 1d13693 commit d0bd330

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
layout: post
3+
title: "Unreachable Switch Statement Default Cases"
4+
date: 2021-03-24
5+
---
6+
7+
Standard C and C++ requires `switch` statements to behave harmlessly if on a value that not covered by one of their `case`'s.
8+
For example, in this case
9+
```cpp
10+
switch( i ) {
11+
case 0:
12+
j+=k;
13+
break;
14+
case 1;
15+
j*=k;
16+
break;
17+
case 2:
18+
j/=k;
19+
break;
20+
}
21+
```
22+
If `i` wasn't 0, 1, or 2, the `switch` statement would just roll on by without anything happening to `j`.
23+
24+
Behaving harmlessly is good, except that it's not free.
25+
In practice, this means that every time the `switch` statement executes it has to bounds-check `i`.
26+
In really tight loops, for example in a [byte-code interpreter](https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables) where each case statement represents an instruction on a virtual CPU, this bounds-checking can add up!
27+
28+
Enter [`__builtin_unreachable()`](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html), a compiler intrinsic that lets you double-dog swear that a section of code will never execute.
29+
It's not standard, but GCC and Clang at least both know about it.
30+
Getting the compiler to take your word that certain execution paths will never execute is useful for all sorts of mischief, but I wondered whether it might prove useful in this particular problem of disabling `switch` bounds-checking.
31+
32+
Basically this would look like something along the lines of,
33+
```cpp
34+
switch( i ) {
35+
case 0:
36+
j+=k;
37+
break;
38+
case 1;
39+
j*=k;
40+
break;
41+
case 2:
42+
j/=k;
43+
break;
44+
default:
45+
__builtin_unreachable();
46+
}
47+
```
48+
49+
50+
To the compiler-explorer-&-quick-bench-mobile!!
51+
:bat:
52+
53+
Here's an assembly snippet from a small example I threw together [on Godbolt](https://godbolt.org/z/TrqKv77G6).
54+
```
55+
unreachable_default(int):
56+
mov edi, edi
57+
mov eax, DWORD PTR CSWTCH.2[0+rdi*4]
58+
ret
59+
no_default(int):
60+
xor eax, eax
61+
cmp edi, 3
62+
ja .L3
63+
mov edi, edi
64+
mov eax, DWORD PTR CSWTCH.4[0+rdi*4]
65+
```
66+
67+
With the `unreachable_default` function, we can skip a `cmp` (compare) and `ja` (jump if) instruction.
68+
I assume that in the `no_default` function those instructions were being used for the aforementioned bounds-checking.
69+
70+
But, does it go brrr?
71+
[Looking at this graph](https://www.youtube.com/watch?v=sIlNIVXpIns), it does indeed seem to go brrr.
72+
73+
![benchmarking result](/resources/switch-default-microbench.jpg){:width="100%"}
74+
75+
Compiling with Clang, adding an unreachable default case gives ~1.3x speedup [on a toy example](https://quick-bench.com/q/1cObPCIkz3g44gHWFWeTkKDgp1Q).
76+
Even though I was able to see some changes in the compiler output (so `_unreachable_default()` was doing *something*) [I wasn't able to see any speedup on GCC, though](https://quick-bench.com/q/ycTTednxWKAsGKd3JsRG9YH9bx8).
77+
78+
Kind of a nifty trick!
79+
I've gone ahead and pasted it in to some of my projects, like [signagp-lite](https://github.com/mmore500/signalgp-lite), that do byte-code interpretation in a tight loop.
80+
I did add a (debug-mode only) assert in right before the `__builtin_unreachable()`... you know, in order to properly check myself when I inevitably wreck myself.
81+
:man_shrugging:
82+
83+
## Let's Chat
84+
85+
Comments?
86+
Questions?
87+
I'd love to hear about any related hacks you're up to!
88+
89+
I started a twitter thread (right below) so we can chat :phone: :phone: :phone:
90+
91+
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">it&#39;s been forever, but new blog post!!! <br><br>this one on mischief with __builtin_unreachable() default switch cases<br><br>perfect for the extra percent of unsafe bytecode interpreter fun 🍹🏖️💣<a href="https://t.co/rBJCM8nnJt">https://t.co/rBJCM8nnJt</a></p>&mdash; Matthew A Moreno (@MorenoMatthewA) <a href="https://twitter.com/MorenoMatthewA/status/1374891036934295555?ref_src=twsrc%5Etfw">March 25, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
14.7 KB
Loading

0 commit comments

Comments
 (0)