Here, you can use “virtual machine” as in “Java Virtual Machine”, not as in virtualization. We will play with the virtual machine described in the paper Tracing the Meta-Level: PyPy’s Tracing JIT Compiler by C.F. Bolz, A. Cuni, M. Fijalkowski and A. Rigo (it’s a great read by the way).
PyPy is a fascinating project, too complex to describe here. Among other things, PyPy can take any interpreter written in a subset of Python, translate it to C, and automatically generate a JIT compiler for this language. Does it sound too good to be true? Let’s try this.
- grab PyPy source code
- create the interpreter in pypy/translator/goal/target-toy.py with the following code:
</pre> <pre style="text-align: justify;">import os, sys import autopath import py # these are the opcodes for the interpreted language JUMP_IF_A = 1 MOV_A_R = 2 MOV_R_A = 3 ADD_R_TO_A = 4 DECR_A = 5 RETURN_A = 6 <em>from pypy.rlib.jit import JitDriver tlrjitdriver = JitDriver(greens = ['pc', 'bytecode'], reds = ['a', 'regs'])</em> # the main interpreter loop def interpret(bytecode, a): regs =  * 256 pc = 0 while True: <em> tlrjitdriver.jit_merge_point(bytecode=bytecode, pc=pc, a=a, regs=regs) </em> opcode = bytecode[pc] pc += 1 if opcode == JUMP_IF_A: target = bytecode[pc] pc += 1 if a: <em> if target<pc: tlrjitdriver.can_enter_jit(bytecode=bytecode, pc=target, a=a, regs=regs) </em> pc = target elif opcode == MOV_A_R: n = bytecode[pc] pc += 1 regs[n] = a elif opcode == MOV_R_A: n = bytecode[pc] pc += 1 a = regs[n] elif opcode == ADD_R_TO_A: n = bytecode[pc] pc += 1 a += regs[n] elif opcode == DECR_A: a -= 1 elif opcode == RETURN_A: return a # __________ Entry point __________ def entry_point(argv): # the program we want to interpret # it computes the square of its argument bytecode = [ MOV_A_R, 0, # i = a MOV_A_R, 1, # copy of ’a’ # 4: MOV_R_A, 0, # i-- DECR_A, MOV_A_R, 0, MOV_R_A, 2, # res += a ADD_R_TO_A, 1, MOV_A_R, 2, MOV_R_A, 0, # if i!=0: goto 4 JUMP_IF_A, 4, MOV_R_A, 2, RETURN_A ] result = interpret(bytecode, int(argv)) print result return 0 def jitpolicy(driver): from pypy.jit.metainterp.policy import JitPolicy return JitPolicy() # _____ Define and setup target ___ def target(*args): return entry_point, None # main function, if this script is called from the command line if __name__ == '__main__': entry_point(sys.argv)</pre> <div><span style="color: #000000;">
15 thoughts on “Creating a toy virtual machine with PyPy”
Interesting, short but nice example
Small question, it appears that the native pypy runs are returning a different value at the end? Have I missed something?
That’s right, because the C and C-jit versions are 32-bit binaries and the translation process probably does not take into account the overflow.
RPython has well documented overflow semantics – you can get overflow detection, but not automatic promotion to long. Here is the description http://codespeak.net/pypy/dist/pypy/doc/coding-guide.html#integer-types
It seems a little unfair to do the comparison in that way, surely arithmetic with more digits is more computationally expensive.
Will you be doing more articles about using PyPy?
Probably. Right now I’m just playing with it, but I’d like to dive deeper.
It’s great to get the wrong answer quickly :>
PyPy FAILED :
is it really necessary to sound like you’re commenting on youtube?
Yep finally …