JIT is different to a byte code interpreter.
Consider the following C function:
int sum() {
return 5 + 6;
}
This will be compiled directly machine code. The exact instructions on say x86 and ARM processors will be different.
If we wrote a basic bytecode interpreter it might look something like this:
for(;;) {
switch(*currentInstruction++) {
case OP_PUSHINT:
*stack++ = nextInt(currentInstruction);
break;
case OP_ADD:
--stack;
stack[-1].add(*stack);
break;
case OP_RETURN:
return stack[-1];
}
}
This can then interpret the following set of instructions:
OP_PUSHINT (5)
OP_PUSHINT (6)
OP_ADD
OP_RETURN
If you compiled the byte code interpreter on both x86 or ARM then you would be able to run the same byte code without doing any further rewriting of the interpreter.
If you wrote a JIT compiler you would need to emit processor specific instructions (machine code) for each supported processor, whereas the byte code interpreter is relying on the C++ compiler to emit the processor specific instructions.