Zarf’s covered a lot of the theory so I won’t repeat it. But I will add a few things.
Modern JIT VMs (including JS ones) generally don’t JIT compile everything. They have a layered approach, starting with a bytecode interpreter. As our web interpreters are VMs inside VMs we skip that stage because it will probably never have the potential to be worth it (though that is unproven, and if anyone wanted to try adding that layer I wouldn’t be opposed.) Our VMs JIT everything, but browser VMs JIT only the code they have determined is run most frequently. They do that by tracing when code is run. Code within a function is easily traced, but they can also trace function calls. But our VMs have one set of code, the VM itself, and another set, the JIT code. The JIT functions are all stored as members of an array, and the VM has a function which says “run the function stored in JITCODE[PROGRAMCOUNTER]”. The browser VMs can trace our VM code, and they can trace the JIT code, but I don’t think that they can trace the link between them, because the function call depends on a variable which could, from the browser’s perspective, be anything at all. To let the browser’s JIT do it’s magic we must aim to reduce the number of changes from our VM to JIT code, and we do that by making our JIT code stop as rarely as we can.
The problem is that branches and jumps are gotos. Now if JS had a goto statement that would make it very easy to produce long stable JIT code, but it doesn’t. Because gotos can go to basically anywhere, whenever we get a branch or a jump the JIT must reluctantly stop. Inform doesn’t have a goto command either (ignoring the fact that you could use assembly to compile a branch/jump), but it does have if statements and loops. But those structures must be assembled by Inform into branches and jumps. What ifvms.js does is recognise those patterns, Idioms in CS terminology, and convert them back into JS ifs and loops. Because we use normally use only those Inform structures, and we don’t manually assemble our own jumps, then the bytecode those structures will be assembled into is very regularly, and easy to detect. With those idioms we can then eliminate the branches/jumps and can continue JIT compiling. ifvms.js is essentially a decompiler, turning low level branches into higher level control structures. At the moment it only recognises idioms for if blocks and while loops, but do/while loops will be easy to add.
Branches and jumps can be taken care of with idioms, which leaves the other main source of JIT stoppages: function calls. To handle them we need function acceleration, and as well as implementing Glulx’s acceleration system, I also plan to experiment with pattern matching, as vaporware’s .NET VM does. (ZLR I think?). Ben’s idea of hashing functions is the same idea. As there will be only a few veneer functions that are accelerated I think it is possible that the browser VMs will be able to trace down into the accelerated veneer and back again. It’s possible that checking whether two AST are the same will work efficiently, but I’m not sure. The only thing we must always stop for is input, and in those cases both the JIT and the VM stop, handing control back to the UI. (Though potentially with a threaded UI system the VM could do background compilation.)
Ben, your example is far more complicated than it needs to be, because loops are really very simple. The condition of the loop is made into a branch, whose target is the instruction after the loop. Then immediately before that a jump instruction is added, whose target is that branch instruction. Without that jump instruction it is identical to an if block. To recognise the loop all we need to do is test whether the last statement in the block is a jump that goes to the condition. The bytecode of do/while loops is simpler because there is no jump, but a little harder to identify because nothing in the bytecode marks where the do statement started - only the target of the final branch.
jsperf.com is indeed a brilliant website and I use it myself. I don’t think it will work well for testing a whole IF VM though. My plan is to write a test suite using the Glulx timer opcodes, and with the same statistical maths as jsperf.com