GoLightly: Building VM-based language runtimes in Go

63
GoLightly Building VM-based language runtimes in Go Eleanor McHugh http://golightly.games-with-brains.net 1 Friday, 15 October 2010

description

A code-heavy exploration of leveraging Go to build virtual machines

Transcript of GoLightly: Building VM-based language runtimes in Go

Page 1: GoLightly: Building VM-based language runtimes in Go

GoLightlyBuilding VM-based language runtimes in Go

Eleanor McHugh

http://golightly.games-with-brains.net

1Friday, 15 October 2010

Page 2: GoLightly: Building VM-based language runtimes in Go

portrait of an artist...

physics major

embedded systems

dynamic languages

dns provisioning

network scaling

questionable taste in music

http://feyeleanor.tel

Eleanor McHugh

2Friday, 15 October 2010

Page 3: GoLightly: Building VM-based language runtimes in Go

today’s menu

an overview of golightly

a crash course in go programming

application virtualisation & soft machines

3Friday, 15 October 2010

Page 4: GoLightly: Building VM-based language runtimes in Go

caveat lector

danger! we’re entering strange territory

our map is missing major landmarks

and will be riddled with inaccuracies

so please tread carefully

try not to disturb the local wildlife

and don’t be put off by the pages of code

4Friday, 15 October 2010

Page 5: GoLightly: Building VM-based language runtimes in Go

golightlyagnostic heterogenous virtualisation networks

5Friday, 15 October 2010

Page 6: GoLightly: Building VM-based language runtimes in Go

go...a systems language by google

productivity, performance, concurrency

lighter than Java, safer than C

6Friday, 15 October 2010

Page 7: GoLightly: Building VM-based language runtimes in Go

...lightlyclean abstractions

geared to performance

non-viral open source license

7Friday, 15 October 2010

Page 8: GoLightly: Building VM-based language runtimes in Go

inspirationprocessor design

sensor and control networks

field-programmable gate arrays

8Friday, 15 October 2010

Page 9: GoLightly: Building VM-based language runtimes in Go

perspirationiterative empirical development

explore -> implement -> test -> benchmark

evolve towards elegance

9Friday, 15 October 2010

Page 10: GoLightly: Building VM-based language runtimes in Go

principlesdecoupling improves scalability

coherence simplifies organisation

optimisations are application specific

10Friday, 15 October 2010

Page 11: GoLightly: Building VM-based language runtimes in Go

agnosticno blessed programming languages

flexible platform abstractions

write once, run everywhere it matters

11Friday, 15 October 2010

Page 12: GoLightly: Building VM-based language runtimes in Go

heterogeneousa system comprises many components

components may differ in purpose and design

but they cooperate to solve problems

12Friday, 15 October 2010

Page 13: GoLightly: Building VM-based language runtimes in Go

virtualisationdesign discrete Turing machines

implement these machines in software

compile programs to run on them

13Friday, 15 October 2010

Page 14: GoLightly: Building VM-based language runtimes in Go

networksmachines cooperate by sending messages

machine states can be serialised as messages

messages transcend process and host boundaries

14Friday, 15 October 2010

Page 15: GoLightly: Building VM-based language runtimes in Go

goa crash course

15Friday, 15 October 2010

Page 16: GoLightly: Building VM-based language runtimes in Go

behind the hype

a statically-typed compiled language

class-free object-orientation

nominal type declaration

structural type inference

garbage collection

concurrency via communication (CSP)

16Friday, 15 October 2010

Page 17: GoLightly: Building VM-based language runtimes in Go

an elegant tool

the safety of a static type system

the feel of a dynamic runtime

the performance of a compiled language

no dependence on runtime libraries

17Friday, 15 October 2010

Page 18: GoLightly: Building VM-based language runtimes in Go

nominal types

primitive (boolean, numeric, pointer)

aggregate (array, slice, map, struct)

functional (closure, channel)

all types can underpin user-defined types

methods are declared on user-defined types

types can be embedded in struct types

18Friday, 15 October 2010

Page 19: GoLightly: Building VM-based language runtimes in Go

package Integer

type Int int

func (i *Int) Add(x int) {*i += Int(x)

}

type Buffer []Int

func (b Buffer) Clone() Buffer {s := make(Buffer, len(b))copy(s, b)return s

}

func (b Buffer) Swap(i, j int) {b[i], b[j] = b[j], b[i]

}

func (b Buffer) Move(i, n int) {if n > len(b) - i {

n = len(b) - i}segment_to_move := b[:i].Clone()copy(b, b[i:i + n])copy(b[n:i + n], segment_to_move)

}

package mainimport “Integer”

func main() {i := Integer.Buffer{0, 1, 2, 3, 4, 5}b := i.Clone()b.Swap(1, 2)b.Move(3, 2)b[0].Add(3)println(“b[0:2] = {”, b[0], “,”, b[1], “}”)

}

produces:b[0:2] = { 6, 4 }

19Friday, 15 October 2010

Page 20: GoLightly: Building VM-based language runtimes in Go

package Vectorimport . “Integer”

type Vector struct {Buffer

}

func (v *Vector) Clone() Vector {return Vector{v.Buffer.Clone()}

}

func (v *Vector) Slice(i, j int) Buffer {return v.Buffer[i:j]

}

func (v *Vector) Replace(o interface{}) {switch o := o.(type) {case Vector:

v = ocase Buffer:

v.Buffer = o}

}

package mainimport “Integer”

func main() {i := Vector{Buffer{0, 1, 2, 3, 4, 5}}b := i.Clone()b.Swap(1, 2)b.Move(3, 2)s := b.Slice(0, 2)s[0].Add(3)b.Replace(s)println(“b[0:2] = {”, b.Buffer[0], “,”, b.Buffer[1], “}”)

}

produces:b[0:2] = { 6, 4 }

20Friday, 15 October 2010

Page 21: GoLightly: Building VM-based language runtimes in Go

structural types

interfaces define method sets

they can be embedded within each other

but do not implement methods

a type can implement many interfaces

type inference determines which if any

all types implement the blank interface

21Friday, 15 October 2010

Page 22: GoLightly: Building VM-based language runtimes in Go

package main

type Adder interface { Add(j int) Subtract(j int) Result() interface{}}

type IntAdder []intfunc (i IntAdder) Add(j int) { i[0] += i[j]}func (i IntAdder) Subtract(j int) { i[0] -= i[j]}func (i IntAdder) Result() interface{} { return i[0]}

type FloatAdder []floatfunc (f FloatAdder) Add(j int) { f[0] += f[j]}func (f FloatAdder) Subtract(j int) { f[0] -= f[j]}func (f FloatAdder) Result() interface{} { return f[0]}

type Calculator struct { Adder}

func main() {c := Calculator{}c.Adder = IntAdder{0, 1, 2, 3, 4, 5}c.Add(1) c.Add(2)c.Subtract(3)println("c.Result() =", c.Result().(int))

c.Adder = FloatAdder{0.0, 1.1, 2.2, 3.3, 4.4, 5.5}c.Add(1)c.Add(2)c.Subtract(3)println("c.Result() =", c.Result())

}

produces:c.Result() = 0c.Result() = (0x10f94,0x34800000)

22Friday, 15 October 2010

Page 23: GoLightly: Building VM-based language runtimes in Go

dynamic typing

type assertions

type switches

runtime reflection

23Friday, 15 October 2010

Page 24: GoLightly: Building VM-based language runtimes in Go

package generaliseimport "fmt"import . "reflect"

func Allocate(i interface{}, limit... int) (n interface{}) {switch v := NewValue(i).(type) {case *SliceValue:

l := v.Cap()if len(limit) > 0 { l = limit[0] }t := v.Type().(*SliceType)n = MakeSlice(t, l, l).Interface()

case *MapValue:n = MakeMap(v.Type().(*MapType)).Interface()

}return

}

func SwapSlices(i interface{}, d, s, n int) {if v, ok := NewValue(i).(*SliceValue); ok {

source := v.Slice(s, s + n)destination := v.Slice(d, d + n)temp := NewValue(Allocate(i, n)).(*SliceValue)ArrayCopy(temp, destination)ArrayCopy(destination, source)ArrayCopy(source, temp)

}}

func Duplicate(i interface{}) (clone interface{}) {if clone = Allocate(i); clone != nil {

switch clone := NewValue(clone).(type) {case *SliceValue:

s := NewValue(i).(*SliceValue)ArrayCopy(clone, s)

case *MapValue:m := NewValue(i).(*MapValue)for _, k := range m.Keys() {

clone.SetElem(k, m.Elem(k))}

}}return

}

24Friday, 15 October 2010

Page 25: GoLightly: Building VM-based language runtimes in Go

package mainimport . “generalise”

func main() {error_text := “panic caused by”defer func() {

if x := recover(); x != nil {fmt.Println(error_text, x)

}}()

s1 := []int{0, 1, 2, 3, 4, 5}fmt.Println("s1 =", s1)s2 := Duplicate(s1)fmt.Println("s2 =", s2, "Duplicate(s1)")SwapSlices(s2, 0, 3, 3)fmt.Println("s2 =", s2, "SwapSlices(s2, 0, 3, 3)")s3 := Allocate(s1, 1)fmt.Println("s3 =", s3, "Allocate(s1, 1)")

m := map[int] int{1: 1, 2: 2, 3: 3, 0: 0, 4: 4, 5: 5}fmt.Println("m =", m)n := Allocate(m)fmt.Println("n =", n, "Allocate(m)")SwapSlices(m, 0, 3, 3)

}

produces:s1 = [0 1 2 3 4 5]s2 = [0 1 2 3 4 5] Duplicate(s1)s2 = [3 4 5 0 1 2] SwapSlices(s2, 0, 3, 3)s3 = [0] Allocate(s1, 1)m = map[3:3 0:0 1:1 4:4 5:5 2:2]n = map[]panic caused by map[3:3 0:0 1:1 4:4 5:5 2:2]

25Friday, 15 October 2010

Page 26: GoLightly: Building VM-based language runtimes in Go

goroutines

concurrent threads of control

launched by the go statement

which returns immediately

each may be a function call or method call

and can communicate via channels

26Friday, 15 October 2010

Page 27: GoLightly: Building VM-based language runtimes in Go

channels

link concurrently executing functions

support sending and/or receiving

only accept items of a specified type

synchronous channels are unbuffered

asynchronous channels are buffered

27Friday, 15 October 2010

Page 28: GoLightly: Building VM-based language runtimes in Go

package generaliseimport . "reflect"

type Results chan interface{}

type SignalSource func(status chan bool)func (s SignalSource) Pipeline() {

done := make(chan bool)defer close(done)go s(done)<-done

}

func (s SignalSource) Multiplex(count int) {done := make(chan bool)defer close(done)go s(done)for i := 0; i < count; i++ {

<- done}

}

type Iteration func(k, x interface{})func (i Iteration) apply(k, v interface{}, c chan bool) {

go func() {i(k, v)c <- true

}()}

func (f Iteration) Each(c interface{}) {switch c := NewValue(c).(type) {case *SliceValue:

count := c.Len()SignalSource(func(done chan bool) {

for i := 0; i < count; i++ {f.apply(i, c.Elem(i).Interface(), done)

}}).Multiplex(count)

case *MapValue:SignalSource(func(done chan bool) {

for _, k := range c.Keys() {f.apply(k, c.Elem(k).Interface(), done)

}}).Multiplex(c.Len())

}}

type Combination func(x, y interface{}) interface{} func (f Combination) Reduce(c, s interface{}) (r Results) {

r = make(Results)go func() {

Iteration(func(k, x interface{}) {s = f(s, x)

}).Each(c)r <- s

}()return

}

28Friday, 15 October 2010

Page 29: GoLightly: Building VM-based language runtimes in Go

type Transformation func(x interface{}) interface{} func (t Transformation) GetValue(x interface{}) Value {

return NewValue(t(x))}

func (t Transformation) Map(c interface{}) interface{} {switch n := NewValue(Allocate(c)).(type) {case *SliceValue:

SignalSource(func(done chan bool) {Iteration(func(k, x interface{}) {

n.Elem(k.(int)).SetValue(t.GetValue(x))}).Each(c)done <- true

}).Pipeline()return n.Interface()

case *MapValue:SignalSource(func(done chan bool) {

Iteration(func(k, x interface{}) {n.SetElem(NewValue(k), t.GetValue(x))

}).Each(c)done <- true

}).Pipeline()return n.Interface()

}return Duplicate(c)

}

package mainimport “fmt”import . “generalise”

var adder Combination = func(x, y interface{}) interface{} { return x.(int) + y.(int)}

var multiplier Transformation = func(x interface{}) interface{} { return x.(int) * 2}

func main() {s := []int{0, 1, 2, 3, 4, 5}fmt.Println("s =", s)fmt.Println("sum s =", (<- adder.Reduce(s, 0)).(int))

c := multiplier.Map(s)fmt.Println("c =", c)fmt.Println("sum c =", (<- adder.Reduce(c, 0)).(int))

}

produces:s = [0 1 2 3 4 5]sum s = 15c = [0 2 4 6 8 10]sum c = 30

29Friday, 15 October 2010

Page 30: GoLightly: Building VM-based language runtimes in Go

tooling

gotest is a testing framework

which also supports benchmarking

gofmt standardises code layout

godoc formats and serves documentation

goinstall is an automatic package installer

cgo integrates C code with go

30Friday, 15 October 2010

Page 31: GoLightly: Building VM-based language runtimes in Go

software machinesapplication virtualisation 101

31Friday, 15 October 2010

Page 32: GoLightly: Building VM-based language runtimes in Go

system clocksynchronising components

32Friday, 15 October 2010

Page 33: GoLightly: Building VM-based language runtimes in Go

package clockimport "syscall"

type Clock struct {Period int64Count chan int64Control chan boolactive bool

}

func (c *Clock) Start() {if !c.active {

go func() {c.active = truefor i := int64(0); ; i++ {

select {case status := <- c.Control:

c.active = statusdefault:

if c.active {c.Count <- i

}syscall.Sleep(c.Period)

}}

}()}

}

package mainimport . “clock”

func main() {c := Clock{1000, make(chan int64), make(chan bool), false}c.Start()

for i := 0; i < 3; i++ {println("pulse value", <-c.Count, "from clock")

}

println("disabling clock")c.Control <- falsesyscall.Sleep(1000000)println("restarting clock")c.Control <- trueprintln("pulse value", <-c.Count, "from clock")

}

produces:pulse value 0 from clockpulse value 1 from clockpulse value 2 from clockdisabling clockrestarting clockpulse value 106 from clock

33Friday, 15 October 2010

Page 34: GoLightly: Building VM-based language runtimes in Go

instruction setspecifying operation sequences

34Friday, 15 October 2010

Page 35: GoLightly: Building VM-based language runtimes in Go

package instructionsimport "fmt"

type Operation func(o []int)

type Executable interface {Opcode() intOperands() []intExecute(op Operation)

}

const INVALID_OPCODE = -1

type Instruction []intfunc (i Instruction) Opcode() int {

if len(i) == 0 {return INVALID_OPCODE

}return i[0]

}

func (i Instruction) Operands() []int {if len(i) < 2 {

return []int{}}return i[1:]

}

func (i Instruction) Execute(op Operation) {op(i.Operands())

}

type Assembler struct {opcodes map[string] intnames map[int] string

}

func NewAssember(names... string) (a Assembler) {a = Assembler{ make(map[string] int), make(map[int] string) }a.Define(names...)return

}

func (a Assembler) Assemble(name string, params... int)(i Instruction) {

i = make(Instruction, len(params) + 1) if opcode, ok := a.opcodes[name]; ok { i[0] = opcode } else { i[0] = INVALID_OPCODE } copy(i[1:], params) return }

35Friday, 15 October 2010

Page 36: GoLightly: Building VM-based language runtimes in Go

func (a Assembler) Define(names... string) {for _, name := range names {

a.opcodes[name] = len(a.names)a.names[len(a.names)] = name

}}

func (a Assembler) Disassemble(e Executable) (s string) {if name, ok := a.names[e.Opcode()]; ok {

s = nameif params := e.Operands(); len(params) > 0 {

s = fmt.Sprintf("%v\t%v", s, params[0])for _, v := range params[1:] {

s = fmt.Sprintf("%v, %v", s, v)}

}} else {

s = "unknown"}return

}

type Program []Executablefunc (p Program) Disassemble(a Assembler) {

for _, v := range p {fmt.Println(a.Disassemble(v))

}}

package mainimport . “instructions”

func main() {a := NewAssembler("noop", "load", "store")p := Program{ a.Assemble("noop"),

a.Assemble("load", 1),a.Assemble("store", 1, 2),a.Assemble("invalid", 3, 4, 5) }

p.Disassemble(a)for _, v := range p {

if len(v.Operands()) == 2 {v.Execute(func(o []int) {

o[0] += o[1]})println("op =", v.Opcode(), "result =", v.Operands()[0])

}}

}

produces:noopload!! 1store! 1, 2unknownop = 2 result = 3

36Friday, 15 October 2010

Page 37: GoLightly: Building VM-based language runtimes in Go

CISCsemantically rich instructions

complex memory addressing modes

compact binary code

37Friday, 15 October 2010

Page 38: GoLightly: Building VM-based language runtimes in Go

RISCseparate IO and data processing

register-to-register instructions

load/store memory access

38Friday, 15 October 2010

Page 39: GoLightly: Building VM-based language runtimes in Go

VLIWmultiple operations per instruction

compiler statically determines parallelism

simplifies control logic

39Friday, 15 October 2010

Page 40: GoLightly: Building VM-based language runtimes in Go

memorystoring data and instructions

40Friday, 15 October 2010

Page 41: GoLightly: Building VM-based language runtimes in Go

subtletiesvon Neumann

Harvard

indirection bits

41Friday, 15 October 2010

Page 42: GoLightly: Building VM-based language runtimes in Go

package memoryimport "fmt"import . "reflect"import "unsafe"

var _BYTE_SLICE = Typeof([]byte(nil))var _SLICE_TYPE = Typeof(SliceHeader{})

func Data(addr unsafe.Pointer) []byte {return unsafe.Unreflect(_BYTE_SLICE, addr).([]byte)

}

func Resize(h *SliceHeader, d, m int) {h.Len /= dh.Len *= m

}

func Serialise(i interface{}) []byte {switch i := NewValue(i).(type) {case *SliceValue:

h := Header(unsafe.Pointer(i.Addr()))t := i.Type().(*SliceType)Resize(&h, 1, int(t.Elem().Size()))return Data(unsafe.Pointer(&h))

}return nil

}

func Overwrite(i interface{}, b []byte) interface{} {switch i := NewValue(i).(type) {case *SliceValue:

h := Header(unsafe.Pointer(&b))t := i.Type().(*SliceType)Resize(&h, int(t.Elem().Size()), 1)return unsafe.Unreflect(t, unsafe.Pointer(&h))

}return nil

}

42Friday, 15 October 2010

Page 43: GoLightly: Building VM-based language runtimes in Go

package mainimport “fmt”import . "memory"

type Memory []int

func main() { m := make(Memory, 2) fmt.Println("m (cells) =", len(m), "of", cap(m), ":", m) b := Serialise(m) fmt.Println("b (bytes) =", len(b), "of", cap(b), ":", b)

n := Overwrite(m, []byte{0, 0, 0, 1, 0, 0, 0, 1}).(Memory) fmt.Println("n (cells) =", len(n), "of", cap(n), ":", n)

b = Serialise(n) fmt.Println("b (bytes) =", len(b), "of", cap(b), ":", b)}

produces:m (cells) = 2 of 2 : [0 0]b (bytes) = 8 of 2 : [0 0 0 0 0 0 0 0]n (cells) = 2 of 8 : [16777216 16777216]b (bytes) = 8 of 8 : [0 0 0 1 0 0 0 1]

43Friday, 15 October 2010

Page 44: GoLightly: Building VM-based language runtimes in Go

processor coretying it all together

44Friday, 15 October 2010

Page 45: GoLightly: Building VM-based language runtimes in Go

package processorimport . “instructions”import . “memory”

const PROCESSOR_READY = 0const PROCESSOR_BUSY = 1const CALL_STACK_UNDERFLOW = 2const CALL_STACK_OVERFLOW = 4const ILLEGAL_OPERATION = 8const INVALID_ADDRESS = 16

type Processor interface {! Run(p []Executable)}

type Core struct {Ticks! ! intRunning! boolPC, Flags! intCS! ! []intM! ! MemoryOP! ! Executable

}

func NewCore(CSSize, MSize int) ProcessorCore {return Core{

CS: make([]int, 0, Calls)},M: make(Memory, MSize),

}}

func (p *Core) Reset() {p.Running = falsep.Flags = PROCESSOR_READY

}

func (c *Core) RunExclusive(p []Executable, f func()) {defer func() {

c.Running = falseif x := recover(); x != nil {

c.Flags &= x.(int)}

}()if c.Running {

panic(PROCESSOR_BUSY)}c.Running = truefor c.PC = 0; c.Running; {

c.LoadInstruction(p)f()c.Ticks++

}}

func (c *Core) LoadInstruction(program []Executable) {if c.PC >= len(program) {

panic(PROCESSOR_READY)}c.Executable = program[c.PC]c.PC++

}

45Friday, 15 October 2010

Page 46: GoLightly: Building VM-based language runtimes in Go

package processor

func (c *Core) Goto(addr int) {! c.PC = addr}

func (c *Core) Call(addr int) {if top := len(c.CS); top < cap(c.CS) - 1 {

c.CS = c.CS[:top + 1]c.CS[top] = c.PCc.PC = addr

} else {panic(CALL_STACK_OVERFLOW)

}}

func (c *Core) TailCall(addr int) {! c.CS[len(c.CS) - 1] = c.PC! c.PC = addr}

func (c *Core) Return() {! if top := len(c.CS); top > 0 {! ! c.PC, c.CS = c.CS[top - 1], c.CS[:top]! } else {! ! panic(CALL_STACK_UNDERFLOW)! }}

46Friday, 15 October 2010

Page 47: GoLightly: Building VM-based language runtimes in Go

package mainimport . “processor”import . “instructions”

const (CALL = iotaGOTOMOVERETURN

)

var dispatcher = func() {switch c.Opcode() {case CALL:

c.Execute(func(o []int) { c.Call(o[0]) })case GOTO:

c.Execute(func(o []int) { c.Goto(o[0]) })case MOVE:

c.Execute(func(o []int) { c.Goto(c.PC + o[0]) })case RETURN:

c.Execute(func(o []int) { c.Return() })default:

panic(ILLEGAL_OPERATION)}

}

func main() {c := NewCore(10, 8)p := []Executable{

Instruction{CALL, 2},Instruction{GOTO, 5},Instruction{MOVE, 2},Instruction{RETURN},Instruction{MOVE, -1},

}c.RunExclusive(p, dispatcher)fmt.Println("Instructions Executed:", c.Ticks)fmt.Println("PC =", c.PC)if c.Flags | PROCESSOR_READY == PROCESSOR_READY {

fmt.Println("Core Ready")} else {

fmt.Println("Core Error:", c.Flags)}

}

produces:top = 0cap(c.CS) -1 = 9Instructions Executed: 2PC = 5Core Ready

47Friday, 15 October 2010

Page 48: GoLightly: Building VM-based language runtimes in Go

accumulator machine1-operand instructions

data from memory combined with accumulator

result stored in accumulator

48Friday, 15 October 2010

Page 49: GoLightly: Building VM-based language runtimes in Go

package accmachineimport . “processor”import . “memory”

const (CONSTANT = iotaLOAD_VALUESTORE_VALUEADD

)

type AccMachine struct {! Core! AC! ! ! ! int}

func NewAccMachine(CSSize, MSize int) *AccMachine {! return &AccMachine{NewCore(CSSize, MSize), 0}}

func (a *AccMachine) Run(program []Executable) {a.RunExclusive(program, func() {

switch a.Opcode() {case CONSTANT:

a.Execute(func(o []int) {a.AC = o[0]

})case LOAD_VALUE:

a.Execute(func(o []int) {a.AC = a.M[o[0]]

})case STORE_VALUE:

a.Execute(func(o []int) {a.M[o[0]] = a.AC

})case ADD:

a.Execute(func(o []int) {a.AC += a.M[o[0]]

})default:

panic(ILLEGAL_OPERATION)}

})}

49Friday, 15 October 2010

Page 50: GoLightly: Building VM-based language runtimes in Go

package mainimport . “accmachine”import . “instructions”

func main() {a := NewAccMachine(10, 8)p := []Executable{

Instruction{CONSTANT, 27},Instruction{STORE_VALUE, 0},Instruction{CONSTANT, 13},Instruction{STORE_VALUE, 1},Instruction{CONSTANT, 10},Instruction{ADD, 1},Instruction{ADD, 0},Instruction{STORE_VALUE, 2},

}a.Run(p)fmt.Println("accumulated value =", a.AC)

}

produces:accumulated value = 50

50Friday, 15 October 2010

Page 51: GoLightly: Building VM-based language runtimes in Go

stack machine0-operand instructions

data popped from stack

results pushed on stack

51Friday, 15 October 2010

Page 52: GoLightly: Building VM-based language runtimes in Go

package smachineimport . “processor”

func (s *StackMachine) Run(program []Executable) {s.RunExclusive(program, func() {

switch s.Opcode() {case CONSTANT:

s.Execute(func(o []int) {s.Push(o[0])

})case PUSH_VALUE:

s.Execute(func(o []int) {s.Push(s.M[o[0]])

})case POP_VALUE:

s.Execute(func(o []int) {s.Pop(s.M[o[0]])

})case ADD:

s.Execute(func(o []int) {l := len(s.DS)s.DS[l - 2] += s.DS[l - 1]s.DS = s.DS[:l - 1]

})default:

panic(ILLEGAL_OPERATION)}

})}

type StackMachine struct {CoreDS! []int

}

func (s *StackMachine) Push(v int) {top := len(s.DS)s.DS, s.DS[top] = s.DS[:top + 1], v

}

func (s *StackMachine) Pop(addr int) {top := len(s.DS) - 1s.M[addr], s.DS = s.DS[top], s.DS[:top]

}

const (CONSTANT = iotaPUSH_VALUEPOP_VALUEADD

)

func NewStackM(CSSize, DSSize, MSize int) *StackMachine {return &StackMachine{

DS: make([]int, 0, DSSize),Core: NewCore(CSSize, MSize),

}}

52Friday, 15 October 2010

Page 53: GoLightly: Building VM-based language runtimes in Go

package mainimport . “smachine”import . “instructions”

func main() {s := NewStackM(10, 10, 8)p := []Executable{

Instruction{CONSTANT, 27},Instruction{CONSTANT, 13},Instruction{CONSTANT, 10},Instruction{ADD},Instruction{ADD},

}s.Run(p)fmt.Println("data stack =", s.DS)

}

produces:registers = [50 13 10 0 0 0 0 0 0 0]

53Friday, 15 October 2010

Page 54: GoLightly: Building VM-based language runtimes in Go

register machinemulti-operand instructions

data read from memory into registers

operator combines registers and stores

54Friday, 15 October 2010

Page 55: GoLightly: Building VM-based language runtimes in Go

package rmachineimport . “processor”

func (r *RegisterMachine) Run(program []Executable) {! r.RunExclusive(program, func() {! ! switch r.Opcode() {! ! case CONSTANT:! ! ! r.Execute(func(o []int) {! ! ! ! r.R[o[0]] = o[1]! ! ! })! ! case LOAD_VALUE:! ! ! r.Execute(func(o []int) {! ! ! ! r.R[o[0]] = r.M[o[1]]! ! ! })! ! case STORE_VALUE:! ! ! r.Execute(func(o []int) {! ! ! ! r.M[o[0]] = r.R[o[1]]! ! ! })! ! case ADD:! ! ! r.Execute(func(o []int) {! ! ! ! r.R[o[0]] += r.R[o[1]]! ! ! })! ! default:! ! ! panic(ILLEGAL_OPERATION)! ! }! })}

type RegisterMachine struct {! Core! R! ! Memory}

const (CONSTANT = iotaLOAD_VALUESTORE_VALUEADD

)

func NewRMachine(CSSize, RSize, MSize int) *RegisterMachine {return &RegisterMachine{

NewCore(CSSize, MSize),make([]int, RSize)

}}

55Friday, 15 October 2010

Page 56: GoLightly: Building VM-based language runtimes in Go

package mainimport . “rmachine”import . “instructions”

func main() {r := NewRMachine(10, 10, 8)p := []Executable{

Instruction{CONSTANT, 0, 27},Instruction{CONSTANT, 1, 13},Instruction{CONSTANT, 2, 10},Instruction{ADD, 0, 1},Instruction{ADD, 0, 2},

}r.Run(p)fmt.Println("registers =", r.R)

}

produces:registers = [50 13 10 0 0 0 0 0 0 0]

56Friday, 15 October 2010

Page 57: GoLightly: Building VM-based language runtimes in Go

transport triggeringregister machine architecture

exposes internal buses and components

operations are side-effects of internal writes

57Friday, 15 October 2010

Page 58: GoLightly: Building VM-based language runtimes in Go

vector machinemulti-operand instructions

data vectors read from memory into registers

operations combine registers

58Friday, 15 October 2010

Page 59: GoLightly: Building VM-based language runtimes in Go

package vmachineimport . “processor”

func (v *VectorMachine) Run(program []Executable) {v.RunExclusive(program, func() {

switch v.Opcode() {case CONSTANT:

v.Execute(func(o []int) { v.Load(o[0], o[1:]) })case LOAD_VALUE:

v.Execute(func(o []int) {v.Load(o[0], v.M[o[1]:o[1] + o[2]])

})case STORE_VALUE:

v.Execute(func(o []int) {copy(v.M[o[0]:], v.R[o[1]])

})case ADD:

v.Execute(func(o []int) {a, b := v.R[o[0]], v.R[o[1]]if len(a) < len(b) {

for i, x := range a { a[i] = x + b[i] }} else {

for i, x := range b { a[i] += x }}

})! ! default:! ! ! panic(ILLEGAL_OPERATION)! ! }! })}

type VectorMachine struct {! Core! R! ! []Memory}

func (v *VectorMachine) Load(r int, m Memory) {! v.R[r] = make(Memory, len(m))! copy(v.R[r], m)}

func NewVMachine(CSSize, RSize, MSize int) *VectorMachine {! return &VectorMachine{

NewCore(CSSize, MSize),make([]Memory, RSize)

}}

const (CONSTANT = iotaLOAD_VALUESTORE_VALUEADD

)

59Friday, 15 October 2010

Page 60: GoLightly: Building VM-based language runtimes in Go

package mainimport . “vmachine”import . “instructions”

func main() {r := NewVMachine(10, 10, 8)p := []Executable{

Instruction{CONSTANT, 0, 27},Instruction{CONSTANT, 1, 13},Instruction{CONSTANT, 2, 10},Instruction{ADD, 0, 1},Instruction{ADD, 0, 2},

}r.Run(p)fmt.Println("registers =", r.R)

}

produces:vectors = [[50 50 50] [13 10 27] [10 27 13] [] [] [] [] [] [] []]

60Friday, 15 October 2010

Page 61: GoLightly: Building VM-based language runtimes in Go

superscalarmultiple execution units

processor caching

out-of-order execution

61Friday, 15 October 2010

Page 62: GoLightly: Building VM-based language runtimes in Go

close to the machineinterrupts

transport buses

peripheral drivers

62Friday, 15 October 2010

Page 63: GoLightly: Building VM-based language runtimes in Go

finding out more

http://golightly.games-with-brains.net

http://github.com/feyeleanor/GoLightly

twitter://#golightly

wikipedia

google

63Friday, 15 October 2010