Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to make a simple virtual machine

How to make a simple virtual machine

Teaches important virtual machine topics like interpretation, optimization and JIT-compilation. We'll use the Brainf**k language and build simple VMs in Python and, finally, a machine code JIT-compiler in C++ using GNU Lightning.

Link to code:
https://github.com/cslarsen/brainfuck-jit

Link to MeetUp:
http://www.meetup.com/Stavanger-Software-Developers-Meetup/events/224303407/

Full Abstract:
Virtual machines are everywhere! While they power most modern programming languages, they also show up in unexpected places like: Network packet filters, regular expression engines and all your big name games.

A virtual machine is a piece of software that executes really simple instructions, just like a CPU. Steve Jobs said it best: "It takes these really simple-minded instructions – 'Go fetch a number, add it to this one, put the result there' – but if you execute them at a rate of, say, 1 million per second, the results appear to be magic."

In this talk we'll uncover some of their mystery by building a dead simple machine, from scratch, using Python. You'll get a glimpse of what happens under the hood of VMs like the JVM and CLR, and you'll learn enough so that you can go home and build your own VM!

Near the end of the talk we'll also briefly touch on subjects like bytecode optimization, JIT-compilation and discuss usage applications.

Images of the 6502 CPU were taken from http://www.visual6502.org

Christian Stigen Larsen

August 18, 2015
Tweet

More Decks by Christian Stigen Larsen

Other Decks in Programming

Transcript

  1. How to make a simple virtual machine Christian Stigen Larsen

    — Roxar Software Solutions Stavanger Software Developers, Aug. 2015
  2. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  3. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  4. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  5. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  6. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  7. while  True:      op  =  next_instruction()      

         if  op  ==  foo:          do_foo()      elif  op  ==  bar:          do_bar()      elif          …    
  8. > Move right < Move left + Increment - Decrement

    . Print , Read [ Start loop ] End loop Instructions
  9. 0 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  10. 0 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  11. 1 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  12. 1 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  13. 1 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  14. 1 4 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  15. 1 3 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  16. 1 3 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  17. 1 2 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  18. 1 2 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 … … + > - - …
  19. +++++++++++++++++++++++++   +++++++++++++++++++++++++   ++++++++++++++++++++++.>+   +++++++++++++++++++++++++   +++++++++++++++++++++++++  

    ++++++++++++++++++.>+++++   +++++++++++++++++++++++++   +++++++++++++++++++++++++   +++++++++++++++++++++..>+   +++++++++.
  20. +++++++++++++++++++++++++   +++++++++++++++++++++++++   ++++++++++++++++++++++.>+   +++++++++++++++++++++++++   +++++++++++++++++++++++++  

    ++++++++++++++++++.>+++++   +++++++++++++++++++++++++   +++++++++++++++++++++++++   +++++++++++++++++++++..>+   +++++++++. HELL
  21. +++++++++++++++++++++++++   +++++++++++++++++++++++++   ++++++++++++++++++++++.>+   +++++++++++++++++++++++++   +++++++++++++++++++++++++  

    ++++++++++++++++++.>+++++   +++++++++++++++++++++++++   +++++++++++++++++++++++++   +++++++++++++++++++++..>+   +++++++++. HELL
  22. +++++++++++++++++++++++++   +++++++++++++++++++++++++   ++++++++++++++++++++++.>+   +++++++++++++++++++++++++   +++++++++++++++++++++++++  

    ++++++++++++++++++.>+++++   +++++++++++++++++++++++++   +++++++++++++++++++++++++   +++++++++++++++++++++..>+   +++++++++. HELL
  23. +++++++++++++++++++++++++   +++++++++++++++++++++++++   ++++++++++++++++++++++.>+   +++++++++++++++++++++++++   +++++++++++++++++++++++++  

    ++++++++++++++++++.>+++++   +++++++++++++++++++++++++   +++++++++++++++++++++++++   +++++++++++++++++++++..>+   +++++++++. HELL
  24. Loops 2 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  25. Loops 2 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  26. Loops 2 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  27. Loops 1 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  28. Loops 1 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  29. Loops 1 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  30. Loops 1 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  31. Loops 1 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  32. Loops 0 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  33. Loops 0 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  34. Loops 0 1 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 … … [ . - ] …
  35. Interpreter class  Machine:      def  __init__(…):      

       self.memory  =  [0]*memsize          self.mptr  =  0            self.code  =  …          self.cptr  =  0          self.stack  =  deque()
  36. class  Machine:      def  __init__(…):        

     self.memory  =  [0]*memsize          self.mptr  =  0            self.code  =  …          self.cptr  =  0          self.stack  =  deque()
  37. class  Machine:      def  __init__(…):        

     self.memory  =  [0]*memsize          self.mptr  =  0            self.code  =  …          self.cptr  =  0          self.stack  =  deque()
  38. class  Machine:      def  __init__(…):        

     self.memory  =  [0]*memsize          self.mptr  =  0            self.code  =  …          self.cptr  =  0          self.stack  =  deque()
  39. def  run(self):      while  True:        

     instruction  =  self.next()          self.dispatch(instruction)
  40. def  run(self):      while  True:        

     instruction  =  self.next()          self.dispatch(instruction)
  41. def  run(self):      while  True:        

     instruction  =  self.next()          self.dispatch(instruction)
  42. def  run(self):      while  True:        

     instruction  =  self.next()          self.dispatch(instruction)
  43. def  next(self):          instruction  =  self.code[self.cptr]  

           self.cptr  +=  1          return  instruction
  44. def  next(self):          instruction  =  self.code[self.cptr]  

           self.cptr  +=  1          return  instruction
  45. def  next(self):          instruction  =  self.code[self.cptr]  

           self.cptr  +=  1          return  instruction
  46. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  47. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  48. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  49. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  50. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  51. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  52. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  53. def  dispatch(self,  i):      if  i  ==  «>»:  

           self.mptr  +=  1      elif  i  ==  «<»:          self.mptr  -­‐=  1      elif  i  ==  «+»:          self.memory[self.mptr]  +=  1      elif  i  ==  «-­‐»:          self.memory[self.mptr]  -­‐=  1      elif  i  ==  «.»:          value  =  self.memory[self.mptr]          sys.stdout.write(value)      elif  i  ==  «,»:          value  =  sys.stdint.read(1)          self.memory[self.mptr]  =  value  
  54. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()
  55. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()
  56. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()
  57. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()
  58. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block() [  …  [  …  ]  …  ]
  59. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          count  =  1          while  count  >  0:              i  =  self.next()              if  i  ==  «[»:                  count  +=  1              elif  i  ==  «]»:                  count  -­‐=  1
  60. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()
  61. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()   elif  i  ==  «]»:      return_addr  =  self.stack.pop()      if  self.memory[self.mptr]  !=  0:          self.cptr  =  return_addr
  62. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()   elif  i  ==  «]»:      return_addr  =  self.stack.pop()      if  self.memory[self.mptr]  !=  0:          self.cptr  =  return_addr
  63. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()   elif  i  ==  «]»:      return_addr  =  self.stack.pop()      if  self.memory[self.mptr]  !=  0:          self.cptr  =  return_addr
  64. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()   elif  i  ==  «]»:      return_addr  =  self.stack.pop()      if  self.memory[self.mptr]  !=  0:          self.cptr  =  return_addr
  65. elif  i  ==  «[»:      if  self.memory[self.mptr]  !=  0:

             self.stack.append(self.cptr-­‐1)      elif:          self.skip_block()   elif  i  ==  «]»:      return_addr  =  self.stack.pop()      if  self.memory[self.mptr]  !=  0:          self.cptr  =  return_addr
  66. Register1 Register 2 … Program Counter Frame Stack Register Machine

    Memory I/O self.memory[0]  (kinda) self.memory self.cptr self.stack sys.stdin,  sys.stdout self.memory[1]  (kinda)
  67. Register1 Register 2 … Program Counter Frame Stack Register Machine

    Memory I/O self.memory[0]  (kinda) self.memory self.cptr self.stack sys.stdin,  sys.stdout self.memory[1]  (kinda)
  68. Register1 Register 2 … Program Counter Frame Stack Register Machine

    Memory I/O Stack Machine Data Stack Program Counter Return Stack Memory I/O
  69. Brainf**k to bytecode def  plus():      LOAD_FAST  «ptr»  

       LOAD_CONST  1      INPLACE_ADD      STORE_FAST  «ptr»
  70. Loops label  «start-­‐of-­‐loop»   LOAD_FAST  «memory»        memory[ptr]

      LOAD_FAST  «ptr»   BINARY_SUBSCR   LOAD_CONST  0                    memory[ptr]  ==  0   COMPARE_OP  «==»   POP_JUMP_IF_TRUE  «end-­‐of-­‐loop»   if  memory[ptr]  ==  0:  goto  «end-­‐of-­‐loop»
  71. Loops label  «start-­‐of-­‐loop»   LOAD_FAST  «memory»        memory[ptr]

      LOAD_FAST  «ptr»   BINARY_SUBSCR   LOAD_CONST  0                    memory[ptr]  ==  0   COMPARE_OP  «==»   POP_JUMP_IF_TRUE  «end-­‐of-­‐loop»   if  memory[ptr]  ==  0:  goto  «end-­‐of-­‐loop»
  72. Optimizations >>>> ptr  +=  4 <<<< ptr  -­‐=  4 ++++

    memory[ptr]  +=  4 -­‐  -­‐  -­‐  -­‐ memory[ptr]  -­‐=  4
  73. Register V0 Points to memory cell Register V1 For arithmetic

    movi  V0,  <address  of  first  cell> GNU Lightning
  74. < subi  V0,  1 > addi  V0,  1 + ldr

     V1,  V0   addi  V1,  1   str  V0,  V1 -­‐ ldr  V1,  V0   subi  V1,  1   str  V0,  V1
  75. < subi  V0,  1 > addi  V0,  1 + ldr

     V1,  V0   addi  V1,  1   str  V0,  V1 -­‐ ldr  V1,  V0   subi  V1,  1   str  V0,  V1
  76. < subi  V0,  1 > addi  V0,  1 + ldr

     V1,  V0   addi  V1,  1   str  V0,  V1 -­‐ ldr  V1,  V0   subi  V1,  1   str  V0,  V1
  77. < subi  V0,  1 > addi  V0,  1 + ldr

     V1,  V0   addi  V1,  1   str  V0,  V1 -­‐ ldr  V1,  V0   subi  V1,  1   str  V0,  V1
  78. [ loop_start:      ldr  V1,  V0      beqi

     V1,  0,  loop_end ] loop_start:      ldr  V1,  V0      bnei  V1,  0,  loop_start
  79. [ loop_start:      ldr  V1,  V0      beqi

     V1,  0,  loop_end ]    jmp  loop_start
  80. ++++++++++   >+++[<.>-­‐] mov        (%rbx),%r13   add

           $0xa,%r13   mov        %r13,(%rbx)   add        $0x8,%rbx   mov        (%rbx),%r13   add        $0x3,%r13   mov        %r13,(%rbx)  
  81. ++++++++++   >+++[<.>-­‐] mov        (%rbx),%r13   add

           $0xa,%r13   mov        %r13,(%rbx)   add        $0x8,%rbx   mov        (%rbx),%r13   add        $0x3,%r13   mov        %r13,(%rbx)  
  82. ++++++++++   >+++[<.>-­‐] mov        (%rbx),%r13   add

           $0xa,%r13   mov        %r13,(%rbx)   add        $0x8,%rbx   mov        (%rbx),%r13   add        $0x3,%r13   mov        %r13,(%rbx)  
  83. ++++++++++   >+++[<.>-­‐] mov        (%rbx),%r13   add

           $0xa,%r13   mov        %r13,(%rbx)   add        $0x8,%rbx   mov        (%rbx),%r13   add        $0x3,%r13   mov        %r13,(%rbx)  
  84. ++++++++++   >+++[<.>-­‐] 0x10006f03b:    mov        (%rbx),%r13

      0x10006f03e:   test      %r13,%r13   0x10006f041:   je          0x10006f090   …   0x10006f048:   sub        $0x8,%rbx   …   0x10006f084:   mov        (%rbx),%r13   0x10006f087:   test      %r13,%r13   0x10006f08a:   jne        0x10006f048   0x10006f090:   …
  85. ++++++++++   >+++[<.>-­‐] 0x10006f03b:    mov        (%rbx),%r13

      0x10006f03e:   test      %r13,%r13   0x10006f041:   je          0x10006f090   …   0x10006f048:   sub        $0x8,%rbx   …   0x10006f084:   mov        (%rbx),%r13   0x10006f087:   test      %r13,%r13   0x10006f08a:   jne        0x10006f048   0x10006f090:   …
  86. ++++++++++   >+++[<.>-­‐] 0x10006f03b:    mov        (%rbx),%r13

      0x10006f03e:   test      %r13,%r13   0x10006f041:   je          0x10006f090   …   0x10006f048:   sub        $0x8,%rbx   …   0x10006f084:   mov        (%rbx),%r13   0x10006f087:   test      %r13,%r13   0x10006f08a:   jne        0x10006f048   0x10006f090:   …
  87. ++++++++++   >+++[<.>-­‐] 0x10006f03b:    mov        (%rbx),%r13

      0x10006f03e:   test      %r13,%r13   0x10006f041:   je          0x10006f090   …   0x10006f048:   sub        $0x8,%rbx   …   0x10006f084:   mov        (%rbx),%r13   0x10006f087:   test      %r13,%r13   0x10006f08a:   jne        0x10006f048   0x10006f090:   …