On 7/24/2012 3:33 PM, Dmitry Olshansky wrote:
On 25-Jul-12 01:40, Walter Bright wrote:
On 7/24/2012 12:50 PM, Dmitry Olshansky wrote:
are the same (same as in same number of indirections).
switch (code[pc++])
and
goto code[pc++]()
are the same, too.
It's not. Let's get to assembly then. It's an illustration as I'm no
expert and
may have made some illegal shortcuts in this listing.
goto code[pc++]() is roughly:
mov ecx, [ebx]
inc ebx
jmp [ecx]
jmp code[ecx]
There is no code just jump [ecx]. The offset is included already.
I'm not seeing where "code" is in the asm code you presented.
switch(code[pc++]) is:
mov ecx, [ebx]
inc ebx
mov ecx, [edx+ecx] // assuming jump table is at edx
jump [ecx]
jmp jumptable[ecx]
Damn, it's not the same. If in the above ecx is pc++, here it it code[pc++] i.e.
needs to be loaded. The whole point. And yes, I *didn't* object that it's still
1 JUMP. I'm more focused on extra work being done.
Sorry, I have no idea why it is not the same. jumptable is a static array, and
so does not need to be loaded into a register. And code[] needs to be loaded
from somewhere, and it looks like it's omitted from your example.
Please post source code example so I understand what you mean.
OK. It sure gets confusing.
Here is an article that shows the big picture of to what I want to do:
http://www.complang.tuwien.ac.at/anton/lvas/sem06w/revucky.pdf
It contains some advanced techniques that are out of scope of current topic but
Introduction & Background are short and full of relevant facts.
And for the sample code here it is, casts are ommited for brevity.
First one is "if I had a way to have enforced tail call to function or take
address of label".
Let's assume labels:
//GLOBALS
size_t[] bytecode = firstPassCompile("my_source.ds"); //say it's DMDscript ;)
bool jitted = false;
void run(size_t pc)
{
//so here bytecode is normal bytecode if jitted != true
//for simplicity sake it starts with some number e.g.:
// 0 - op_1
// 1 - op_2, etc.
if(!jitted)
{
int i=0;
//1:1 map of each label that servers specific opcode
static tabulated_ops[] =
[ &L_op1, &L_op2, &L_op3, ... ];
while(notEndOfProgram(bytecode[i]) )
{
size_t n = bytecode[i];
//store real target
bytecode[i] = tabulated_ops[n];
//advance by some size, skipping operands etc.
i += opSize(n);
}
}
//interpret:
goto bytecode[pc];
//<---- never gets here
L_op1:
//do my op1 thing
pc += x;
//DISPATH:
goto bytecode[pc]; // load address at pc & jump to it
L_op2:
//do some other thing
pc += y;
//DISPATH:
goto bytecode[pc]; // load address at pc & jump to it
L_op3:
//my other op, jumps back
pc -= ...;
//DISPATH:
goto bytecode[pc]; // load address at pc & jump to it
...
L_opN:
...
goto bytecode[pc]; // load address at pc & jump to it
}
Now the same thing with switches.
And before you ask -
NO! Single switch won't do as it would have successful branch prediction rate of
as bad as about 2% (see the article!). The more opcode types the worse
prediction is.
I see what you mean, but this could be done with final switch by the compiler,
as I explained to bearophile.
Anyway here it goes:
//GLOBALS
size_t[] bytecode = firstPassCompile("my_source.ds"); //say it's DMDscript ;)
void run(size_t pc)
{
//here we don't JIT to real addresses beforehand
//as jump tables somehow should be good enough
// Okay...
//interpret:
switch(bytecode[pc])
{
L_op1:
case 0:
//do my op1 thing
pc += x;
//DISPATCH:
//here comes trouble - 1st of N nearly identical tables??
switch(bytecode[pc])
{
case 0: goto L_op1;
case 1: goto L_op2;
...
}
L_op2:
case 1:
//do some other thing
pc += y;
//DISPATCH:
//here comes trouble - 2nd table?
switch(bytecode[pc])
{
case 0: goto L_op1;
case 1: goto L_op2;
...
}
L_op3:
case 2:
//my other op, jumps back
pc -= ...;
//DISPATCH:
//here comes trouble - 3rd table?
switch(bytecode[pc])
{
case 0: goto L_op1;
case 1: goto L_op2;
...
}
...
L_opN:
case N-1:
...
//DISPATCH:
//here comes trouble Nth table ... time to count overhead
switch(bytecode[pc])
{
case 0: goto L_op1;
case 1: goto L_op2;
...
}
}//end of giant switch
}
I see what you mean here, too. Thanks for the explanation. It never occurred to
me that one could write code like that, but I see the point, and doing jump
table merging could be done fairly easily. No new language feature is required.