Tamarin Trace Recording - Hooking into the Interpreter

We're finally at the point where we can talk about the tracing aspects of Tamarin. If you are unfamiliar with the idea of tracing, you should probably read this. To quickly sum up: we keep track of backwards branches, and once a certain threshold has been hit, we record the instructions that are being executed, and compile those to native code. Here's how tamarin does the recording. First, we define a method to keep track of a loop edge:

Interpreter.cpp
#define LOOPEDGE(i, x) do { TIMING_START(t_taken) (i).loopedge(x); TIMING_END(t_taken) } while (0)
Since this doesn't make much sense, we should look at a call to LOOPEDGE:
Interpreter.cpp
Interpreter& interp;
InterpState state(f->pc,ip,sp,rp,f);
LOOPEDGE(interp, state);
So here we note the state, and give a call to LOOPEDGE. What's interesting is where LOOPEDGE is called: Only in the Forth words "iftrue", "iffalse", and "switch". This is the case as all ABC opcodes such as IFGT, IFLT, do the comparison first to see if something is less/greater than something. The branch is then based on this boolean result as said in the avm2 spec:

For IFLE:

vm.fs
EXTERN: OP_iftrue tobool IFTRUE NEXT ;
EXTERN: OP_iffalse tobool IFFALSE NEXT ;
EXTERN: OP_ifeq == IFTRUE NEXT ;
EXTERN: OP_ifne == IFFALSE NEXT ;
EXTERN: OP_iflt cmplt IFTRUE NEXT ;
EXTERN: OP_ifle cmple IFTRUE NEXT ;
EXTERN: OP_ifgt cmpgt IFTRUE NEXT ;
EXTERN: OP_ifge cmpge IFTRUE NEXT ;
However, to do such a thing, the loops must be constructed in a different order. Let's take a quick loop at what is executed for a standard "for" and "while" loop. First the "for" loop:
ForLoop.js
for (i = 0; i < 1; i++) {
print(i);
}

ForLoop.abc
8:jump 39
12:Label
// Loop body
39:findpropstrict
41:getproperty
43:pushbyte 1
45:iflt 12

For While Loops:
WhileLoop.js
i = 0
while (i<1) {
i++
}

WhileLoop.abc
15:jump 40
19:label
// Loop body
40:findpropstrict
42:getproperty
44:pushbyte 1
46:iflt 19

Therefore, the only way this works is because we jump to the loop condition which is at the bottom of the loop body. Therefore Tamarin evaluates the condition which results into a true/false jump backwards, which then executes the actual loop body. Finally, let's expand the LOOPEDGE macro:

Interpreter.cpp
LOOPEDGE(interp, state);
#define LOOPEDGE(i, x) do { TIMING_START(t_taken) (i).loopedge(x); TIMING_END(t_taken) } while (0)

do {
TIMING_START(t_taken);
this.loopedge(state);
TIMING_END(t_taken);
} while (0)

void Interpreter::loopedge(InterpState &state) {
}

So we finally have a glimpse at how the trace recorder hooks into the interpreter.