Interpreters (Outline notes) Examples: Basic, Lisp FORTH -> Postscript Shell scripts, elisp, Tcl, Perl etc. Benefits: Simplicity, Portability, Fast turn-around. Cost: Execution speed Why are interpreters slow? Elements of cost per instruction: - Dispatch (fetch,decode, and start) next instruction - Access arguments - Perform function. Goals in implementation: speed vs. portability. Target architecture very important (even if not directly exposed). - Register-rich? - Memory hierarchy? Costs of using underlying memory-based stack? - Indirect jump support? Ideas for making dispatch cheaper: - threaded code (opcode = address) - inline jump to next code - problems doing this in portable way - building combined instructions - use stack architecture: no explicit args, so no decoding. (in addition: we can't index registers in real machine). - ultimately: inline code sequences (really a simple compiler) Ideas for making argument access cheaper: - specialize instructions based on common literal args - attempt register caching (static or dynamic). - dangers in state space (hence code) explosion. /* * machine.c * Extremely simplified Java virtual machine interpreter. * * Many things omitted, including: * - synchronization for threads * - exception handling * - wide arguments * - multiple types of data * * Derived from kaffe by Tim Wilkinson * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK. */ typedef unsigned char bytecode; #define NOP 0 #define ACONST_NULL 1 ... const uint8 insnLen[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...} void virtualMachine(methods* meth, int* args, int* retval) { /* If these can be kept in registers then things will go much * better. */ register bytecode* code; /* code array */ register int* lcl; /* operand stack */ register int* sp; /* operand stack pointer */ register uintp pc = 0; /* current code pointer */ register uintp npc = 0; /* next code pointer * / /* Allocate stack space and locals. */ lcl = alloca(sizeof(int) * (meth->localsz + meth->stacksz)); /* Determine number of arguments */ nargs = meth->...; /* Copy in the arguments */ sp = lcl; args = &args[nargs-1]; for (i = 0; i < nargs; i++) { *(sp++) = *(nargs--); } sp = &lcl[meth->localsz + meth->stacksz]; code = (bytecode*)meth->c.bcode.code; /* Execute the code */ for (;;) { pc = npc; npc = pc + insnLen[code[pc]]; switch(code[pc]) { NOP: break; ACONST_NULL: *(--sp) = 0; break; ... BIPUSH: *(--sp) = (int8)code[pc+1]; break; ... ILOAD: idx = (uint8)code[pc+1]; *(--sp) = *(lcl+idx) break; ... ISTORE: idx = (uint8)code[pc+1]; *(lcl+idx) = *(sp++) break; ... DUP_X1: sp--; *sp = *(sp+1); *(sp+1) = *(sp+2); *(sp+2) = *sp; break; ... IADD: *(++sp) = *sp + *(sp+1) break; ... IINC: idx = (uint8)code[pc+1]; *(lcl+idx) = *(lcl+idx) + (int8)code[pc+2]; break; ... IFEQ: idx = (int16)((code[pc+1] << 8) | code[pc+2]); if (*sp++ == 0) npc = pc+idx; break; ... GETFIELD: idx = (uint16)((pc[1] << 8) | pc[2]); offset = get_field_offset(idx); /* some magic */ *sp = *(*sp+offset); break; ... INVOKESTATIC: idx = (uint16)((pc[1] << 8) | pc[2]); method = get_method_info(idx); /* magic */ nargs = method->...; virtualMachine(method, sp, retval); sp += (nargs -1); *sp = *retval; break; IRETURN: *retval = *sp; goto end; ... } } end: }