A bitecode interpreter is another place where it's nice to have gotos.
Here's the base code without gotos:
typedef enum { ADD, MUL, ..., END } opcode;
void run() {
opcode ins;
while (1) {
ins = fetch_next_inst();
switch (ins) {
case ADD:
perform_addition();
break;
case MUL:
perform_multiplication();
break;
...
case END:
wrap_up();
return;
}
}
}
You have 3 jumps on each loop. From the break to the end of the loop, then from the end to the top, and one from the switch to the right case. The first one might be optimized away, but let's remove it explicitly.
typedef enum { ADD, MUL, ..., END } opcode;
void run() {
opcode ins;
start:
ins = fetch_next_inst();
switch (ins) {
case ADD:
perform_addition();
goto start;
case MUL:
perform_multiplication();
goto start;
...
case END:
wrap_up();
return;
}
}
Assuming a non lousy compiler, we haven't improved anything yet. But now the fun starts. We can go down to one jump for each iteration.
typedef enum { ADD, MUL, ..., END } opcode;
#define NEXT() \
do { \
ins = fetch_next_inst(); \
goto *jump_table[ins]; \
} while(0)
void run() {
opcode ins;
static void *jump_table[] = { &&add_l, &&mul_l, ..., &&end_l };
NEXT();
add_l:
perform_addition();
NEXT();
mul_l:
perform_multiplication();
NEXT();
...
end_l:
wrap_up();
return;
}
Voila! a single jump every time around. Now, depending on what kind of architecture you're running on, the size of the cache, etc, this may or may not be faster.
Granted, this is not the kind of code you write everyday. But sometimes speed matters, and good luck writing this without gotos.