if I undestood it correctly, computed gotos are used to reduce the branching in
interpreter loops(a head moving through data and then executing something
depending on the read value).
>From one of your links:
int interp_switch(unsigned char* code, int initval) {
int pc = 0;
int val = initval;
while (1) {
switch (code[pc++]) { // branch depending on the value
case OP_HALT:
return val;
case OP_INC:
val++;
break; // branch to the end of the switch statement
case OP_DEC:
val--;
break;
case OP_MUL2:
val *= 2;
break;
case OP_DIV2:
val /= 2;
break;
case OP_ADD7:
val += 7;
break;
case OP_NEG:
val = -val;
break;
default:
return val;
}
} // branch to the beginning of the loop. The compiler might optimise
it to make the break jump directly to the top
}
Run
So in this unoptimised case we have two or three branches per interpreted
instruction.
The computed goto allows us to reduce it to only one branch per instruction:
int interp_cgoto(unsigned char* code, int initval) {
/* The indices of labels in the dispatch_table are the relevant opcodes
*/
static void* dispatch_table[] = {
&&do_halt, &&do_inc, &&do_dec, &&do_mul2,
&&do_div2, &&do_add7, &&do_neg};
#define DISPATCH() goto *dispatch_table[code[pc++]]
int pc = 0;
int val = initval;
DISPATCH(); // jump to the next instruction
while (1) {
do_halt:
return val;
do_inc:
val++;
DISPATCH(); // directly jump to the next instruction
do_dec:
val--;
DISPATCH();
do_mul2:
val *= 2;
DISPATCH();
do_div2:
val /= 2;
DISPATCH();
do_add7:
val += 7;
DISPATCH();
do_neg:
val = -val;
DISPATCH();
}
}
Run