@Charles:Thanks a lot for the both links!
@Aurel:No, it isn't.
The article Charles pointed to explains computed
goto's very clearly. In ordinary C code, you don't know the memory address of a label. The
jmp inctruction is hardcoded by the compiler at compile time based on the label's relative offset from the command (
goto) that actually jumps to the label:
goto Label;
................. // some other code
Label:But you still don't know what the real address of the label is.
Computed
goto's allow the programmer to retrieve the real address of the label, store it e.g. in a table (i.e. in an array) or even perform some mathematic operations on it if necessary, and use the address or the resultant math value as the target for the instruction that actually jumps to the label or address computed in a math expression. The
goto syntax in this case is slightly different:
void *target = &&Label; // && retrieves Label address
target += 100; // add 100 bytes to Label address
goto *target; // jump 100 bytes farther in memory than where Label points to
................. // some other code
Label:The abstract you quoted comes from Ed's article and it only discusses briefly why
switch isn't perfect for an ideal interpreting loop. Yet Charles' article does it in much deeper detail and in better English.
I would however like to add two points to what is written in both articles:
1. The usual
break causes two jumps to be performed in this hypothetic loop:
// there's an implicit BeginWhile label here
while (1) {
switch (code[pc++]) {
case OP_ONE:
DoSomething();
break;
default:
DoNothing();
} // there's an implicit EndSwitch label here
} // there's an implicit EndWhile label here- first, to the implicit EndSwitch label, and then, from the EndWhile label to the BeginWhile label where
while (1) resides.
Assuming there's nothing to execute in between the EndSwitch and EndWhile labels, a more reasonable solution will be to use
continue instead of
break - it will spare one extra jump:
// there's an implicit BeginWhile label here
while (1) {
switch (code[pc++]) {
case OP_ONE:
DoSomething();
continue;
default:
DoNothing();
} // there's an implicit EndSwitch label here
} // there's an implicit EndWhile label here- here
continue jumps directly to the implicit BeginWhile label.
2. All these considerations and timings matter only if the code within DoSomething() and DoNothing() is well-optimized, short and very very fast. If it isn't, you won't see any noticeable or measurable improvement from changing
break's to
continue's, or the entire
switch block, to computed
goto's and a jump table.