I heard that some languages go from interpreted to compiled by "unrolling the interpreter loop".
Let's say I have the following pseudo-c-code interpreter for an ast tree.
int interpret(node)
{
switch(node) {
case PLUS:
return interpret(child(0))+interpret(child(1));
case MINUS:
return interpret(child(0))-interpret(child(1));
}
}
How do I unroll this loop to create a compiled program?
I see you all downvoting this like I don't know what I am talking about, but here is a quote from Wikipedia that states exactly what I am describing.
"Factor was originally only interpreted, but is now fully compiled (the non-optimizing compiler basically unrolls the interpreter loop"
"Unrolling a loop" normally means replacing a repetition with a sequence of actions. The loop:
for (int i = 0; i < 4; ++i) {
a[i] = b[i] + c[i];
}
would unroll into the equivalent:
a[0] = b[0] + c[0];
a[1] = b[1] + c[1];
a[2] = b[2] + c[2];
a[3] = b[3] + c[3];
It appears to me that whoever was being quoted by Wikipedia was using the phrase in a somewhat metaphorical sense. So, in that sense...
Your sample would normally be invoked inside a interpreter that is walking a tree of AST nodes, which might look something like this:
ASSIGN
|
+--+---+
| |
REF MINUS
| |
x +--+---+
| |
VAR PLUS
| |
a +--+--+
| |
VAR CONST
| |
b 3
and the interpret
function would have additional options:
int interpret(node) {
switch(node) {
case PLUS:
return interpret(child(0))+interpret(child(1));
case MINUS:
return interpret(child(0))-interpret(child(1));
case ASSIGN:
return set(child(0), interpret(child(1));
case VAR:
return fetch(child(0));
case CONST:
return value(child(0));
...
}
}
If you walk the AST with that interpet
function (actually performing the operations), you're interpreting. But if the function records the actions to be performed, rather than executing them, you're compiling. In pseudocode (actually, pseudocode twice, as I'm assuming a hypothetical stack machine as the compilation target):
string compile(node) {
switch(node) {
case PLUS:
return(compile(child(0))) + compile(child(1)) + ADD);
case MINUS:
return(compile(child(0))) + compile(child(1)) + SUB);
case ASSIGN:
return(PUSHA(child(0))) + compile(child(1)) + STORE);
case REF:
return(PUSHA(child(0)));
case VAR:
return(PUSHA(child(0)) + FETCH);
case CONST:
return(PUSHLIT + value(child(0)));
...
}
}
Invoking compile
on that AST (ignoring any pseudocode typos ;-) would spit out something like:
PUSHA x
PUSHA a
FETCH
PUSHA b
FETCH
PUSHLIT 3
ADD
SUB
STORE
FWIW, I'd tend to think of that as unrolling the AST, rather than unrolling the interpreter, but won't criticize somebody else's metaphor without reading it in context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With