GoLightly: Building VM-based language runtimes in Go

Preview:

DESCRIPTION

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

Citation preview

GoLightlyBuilding VM-based language runtimes in Go

Eleanor McHugh

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

1Friday, 15 October 2010

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

today’s menu

an overview of golightly

a crash course in go programming

application virtualisation & soft machines

3Friday, 15 October 2010

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

golightlyagnostic heterogenous virtualisation networks

5Friday, 15 October 2010

go...a systems language by google

productivity, performance, concurrency

lighter than Java, safer than C

6Friday, 15 October 2010

...lightlyclean abstractions

geared to performance

non-viral open source license

7Friday, 15 October 2010

inspirationprocessor design

sensor and control networks

field-programmable gate arrays

8Friday, 15 October 2010

perspirationiterative empirical development

explore -> implement -> test -> benchmark

evolve towards elegance

9Friday, 15 October 2010

principlesdecoupling improves scalability

coherence simplifies organisation

optimisations are application specific

10Friday, 15 October 2010

agnosticno blessed programming languages

flexible platform abstractions

write once, run everywhere it matters

11Friday, 15 October 2010

heterogeneousa system comprises many components

components may differ in purpose and design

but they cooperate to solve problems

12Friday, 15 October 2010

virtualisationdesign discrete Turing machines

implement these machines in software

compile programs to run on them

13Friday, 15 October 2010

networksmachines cooperate by sending messages

machine states can be serialised as messages

messages transcend process and host boundaries

14Friday, 15 October 2010

goa crash course

15Friday, 15 October 2010

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

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

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

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

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

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

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

dynamic typing

type assertions

type switches

runtime reflection

23Friday, 15 October 2010

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

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

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

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

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

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

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

software machinesapplication virtualisation 101

31Friday, 15 October 2010

system clocksynchronising components

32Friday, 15 October 2010

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

instruction setspecifying operation sequences

34Friday, 15 October 2010

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

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

CISCsemantically rich instructions

complex memory addressing modes

compact binary code

37Friday, 15 October 2010

RISCseparate IO and data processing

register-to-register instructions

load/store memory access

38Friday, 15 October 2010

VLIWmultiple operations per instruction

compiler statically determines parallelism

simplifies control logic

39Friday, 15 October 2010

memorystoring data and instructions

40Friday, 15 October 2010

subtletiesvon Neumann

Harvard

indirection bits

41Friday, 15 October 2010

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

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

processor coretying it all together

44Friday, 15 October 2010

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

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

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

accumulator machine1-operand instructions

data from memory combined with accumulator

result stored in accumulator

48Friday, 15 October 2010

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

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

stack machine0-operand instructions

data popped from stack

results pushed on stack

51Friday, 15 October 2010

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

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

register machinemulti-operand instructions

data read from memory into registers

operator combines registers and stores

54Friday, 15 October 2010

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

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

transport triggeringregister machine architecture

exposes internal buses and components

operations are side-effects of internal writes

57Friday, 15 October 2010

vector machinemulti-operand instructions

data vectors read from memory into registers

operations combine registers

58Friday, 15 October 2010

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

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

superscalarmultiple execution units

processor caching

out-of-order execution

61Friday, 15 October 2010

close to the machineinterrupts

transport buses

peripheral drivers

62Friday, 15 October 2010

finding out more

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

http://github.com/feyeleanor/GoLightly

twitter://#golightly

wikipedia

google

63Friday, 15 October 2010

Recommended