I decided to use a switch statement rather than a jump table because I figured the compiler would optimize the jumps for me, which it appears to do. Apparently this results in better branch prediction than a jump table would. I have not tested this, performance-wise, but that's what the literature I've read indicates.
Anyway, since the switch statement is switching on a bytecode, it's pretty massive, and there can be a lot of local variables depending on which case is true. To illustrate:
while (running)
{
switch (opcode)
{
case 0:
{
int a;
int b;
int c;
int d;
...some routine involving a and b...
}
break;
case 1:
{
int c;
...some routine involving c...
}
break;
case 2:
{
// call interpret();
}
break;
}
}
This causes a stack allocation of at least 16 bytes for the four ints (a, b, c, and d), even if case 1 or 2 are true 99% of the time. There's no way to tell the compiler that case 0 is hardly ever true, and odds are it would still allocate space for case 0's local variables just in case.
In a switch statement with 50 cases and each case equally likely, just one case that allocates a large number of local stack space can quickly eat up memory in deeply nested function calls, since the space is allocated on function entry.
This is hardly a problem with "normal" programs, but in my case the interpreter has about 50 cases and calls itself recursively. With each function entry about 44 bytes (enough for all local variables in the whole function) are allocated, even though most of that memory is never used. This severely limits the depth of recursion when memory is constrained.
The ideal solution would be if the switch statement could allocate the stack space after it evaluates each case.
I may have to work around this using a combination of if/else and switch for the more complex routines.