Computed gotos on Reddit
Walter Bright
newshound2 at digitalmars.com
Tue Jul 24 16:31:22 PDT 2012
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.
More information about the Digitalmars-d
mailing list