golightly
a customisable virtual machine written in Go
Eleanor McHugh
http://slides.games-with-brains.net/
Thursday, 30 May 13
portrait of an artist...
physics major
embedded controllers
software reliability
dynamic languages
network scaling
questionable taste in music
http://github.com/feyeleanor
Eleanor McHugh
Thursday, 30 May 13
go...
small, safety-conscious systems language
concurrency, closures & garbage collection
consistently fast compilation
Thursday, 30 May 13
...lightly
agnostic virtual machine networks
application performance matters
non-viral open source license
Thursday, 30 May 13
agnostic
no blessed programming languages
flexible platform abstractions
write once, run everywhere it matters
Thursday, 30 May 13
heterogeneous
a system comprises many components
components may differ in purpose and design
but they cooperate to solve problems
Thursday, 30 May 13
networks
machines cooperate by sending messages
machine states can be serialised as messages
messages transcend process and host boundaries
Thursday, 30 May 13
inspiration
hardware design
sensor and control networks
field-programmable gate arrays
Thursday, 30 May 13
virtual machine
emulate an existing system
simulate an imagined system
have dynamic control of system state
Thursday, 30 May 13
stack-based lambda calculus processor
threaded value cells in a von neumann memory
all operations are expressions are values
lisp
Thursday, 30 May 13
stack-based processor
threaded word definitions in a von neumann memory
words thread primitives, word pointers and data
forth
Thursday, 30 May 13
stack-based processor with instruction set
harvard memory separates code and data
class loaders convert bytecode to machine state
jvm
Thursday, 30 May 13
linux kernel hypervisor
intel X86 virtualisation with hardware execution
QEMU virtual machine runs in user space
kvm
Thursday, 30 May 13
hello world
Thursday, 30 May 13
package main
import "fmt"
const(
HELLO string = "hello"
WORLD string = "world"
)
func main() {
fmt.Println(HELLO, WORLD)
}
Thursday, 30 May 13
user-defined type
Thursday, 30 May 13
package Integer
type Int int
func (i *Int) Add(x int) {
*i += Int(x)
}
Thursday, 30 May 13
package Integer
type Buffer []Int
func (b Buffer) Eq(o Buffer) (r bool) {
if len(b) == len(o) {
for i := len(b) - 1; i > 0; i-- {
if b[i] != o[i] {
return
}
}
r = true
}
return
}
func (b Buffer) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func (b Buffer) Clone() Buffer {
s := make(Buffer, len(b))
copy(s, b)
return s
}
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)
}
Thursday, 30 May 13
package main
import "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[:2] = {", b[0], ",", b[1], "}")
}
produces:
b[0:2] = { 6, 4 }
Thursday, 30 May 13
package Integer
import "testing"
func TestSwap(t *testing.T) {
i := Buffer{0, 1, 2, 3, 4, 5}
b := i.Clone()
b.Swap(1, 2)
if !b[1:3].Eq(Buffer{2, 1}) {
t.Fatalf("b[:5] = %v", b)
}
}
func TestMove(t *testing.T) {
i := Buffer{0, 1, 2, 3, 4, 5}
b := i.Clone()
b.Move(3, 2)
if !b.Eq(Buffer{3, 4, 0, 1, 2, 5}) {
t.Fatalf("b[:5] = %v", b)
}
}
func TestAdd(t *testing.T) {
i := Buffer{0, 1, 2, 3, 4, 5}
b := i.Clone()
b[0].Add(3)
if b[0] != i[0] + 3 {
t.Fatalf("b[:5] = %v", b)
}
}
Thursday, 30 May 13
embedding
Thursday, 30 May 13
package Vector
import . "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]
}
Thursday, 30 May 13
package Integer
import "testing"
func TestVectorSwap(t *testing.T) {
i := Vector{Buffer{0, 1, 2, 3, 4, 5}}
v := i.Clone()
v.Swap(1, 2)
r := Vector{Buffer{0, 2, 1, 3, 4, 5}}
switch {
case !v.Match(&r):
fallthrough
case !v.Buffer.Match(r.Buffer):
t.Fatalf("b[:5] = %v", v)
}
}
Thursday, 30 May 13
package integer
import "testing"
func BenchmarkVectorClone6(b *testing.B) {
v := Vector{Buffer{0, 1, 2, 3, 4, 5}}
for i := 0; i < b.N; i++ {
_ = v.Clone()
}
}
func BenchmarkVectorSwap(b *testing.B) {
b.StopTimer()
v := Vector{Buffer{0, 1, 2, 3, 4, 5}}
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Swap(1, 2)
}
}
Thursday, 30 May 13
$ go test -test.bench="Benchmark"
PASS
integer.BenchmarkVectorSwap 200000000 8 ns/op
integer.BenchmarkVectorClone6 10000000 300 ns/op
Thursday, 30 May 13
inference
Thursday, 30 May 13
package adder
type Adder interface {
Add(j int)
Subtract(j int)
Result() interface{}
}
type Calculator interface {
Adder
Reset()
}
type AddingMachine struct {
Memory interface{}
Adder
}
Thursday, 30 May 13
package adder
type IAdder []int
func (i IAdder) Add(j int) {
i[0] += i[j]
}
func (i IAdder) Subtract(j int) {
i[0] -= i[j]
}
func (i IAdder) Result() interface{} {
return i[0]
}
func (i IAdder) Reset() {
i[0] = *new(int)
}
Thursday, 30 May 13
package adder
import "testing"
func TestIAdder(t *testing.T) {
error := "Result %v != %v"
i := IAdder{0, 1, 2}
i.Add(1)
if i.Result().(int) != 1 { t.Fatalf(error, i.Result(), 1) }
i.Subtract(2)
if i.Result().(int) != -1 { t.Fatalf(error, i.Result()), -1 }
var r Calculator = IAdder{-1, 1, 2}
for n, v := range r.(IAdder) {
if i[n] != v { t.Fatalf("Adder %v should be %v", i, r) }
}
r.Reset()
if r.Result().(int) != *new(int) {
t.Fatalf(error, r.Result(), *new(int))
}
}
Thursday, 30 May 13
package adder
import "testing"
func TestIAdder(t *testing.T) {
error := "Result %v != %v"
i := IAdder{0, 1, 2}
i.Add(1)
if i.Result() != 1 { t.Fatalf(error, i.Result(), 1) }
i.Subtract(2)
if i.Result() != -1 { t.Fatalf(error, i.Result()), -1 }
var r Calculator = IAdder{-1, 1, 2}
for n, v := range r.(IAdder) {
if i[n] != v { t.Fatalf("Adder %v should be %v", i, r) }
}
r.Reset()
if r.Result() != *new(int) {
t.Fatalf(error, r.Result(), *new(int))
}
}
Thursday, 30 May 13
package adder
type FAdder []float32
func (f FAdder) Add(j int) {
f[0] += f[j]
}
func (f FAdder) Subtract(j int) {
f[0] -= f[j]
}
func (f FAdder) Result() interface{} {
return f[0]
}
func (f FAdder) Reset() {
f[0] = *new(float32)
}
Thursday, 30 May 13
package adder
import "testing"
func TestFAdder(t *testing.T) {
error := "Result %v != %v"
f := FAdder{0.0, 1.0, 2.0}
f.Add(1)
if f.Result() != 1.0 { t.Fatalf(error, f.Result(), 1.0) }
f.Subtract(2)
if i.Result() != -1.0 { t.Fatalf(error, i.Result()), -1.0 }
var r Calculator = FAdder{-1.0, 1.0, 2.0}
for n, v := range r.(FAdder) {
if f[n] != v { t.Fatalf("Adder %v should be %v", f, r) }
}
r.Reset()
if r.Result() != *new(float32) {
t.Fatalf(error, r.Result(), *new(float32))
}
}
Thursday, 30 May 13
package adder
import "testing"
func TestAddingMachine(t *testing.T) {
error := "Result %v != %v"
a := &AddingMachine{ Adder: FAdder{0.0, 1.0, 2.0} }
a.Add(1)
if f, ok := a.Result().(float32); !ok {
t.Fatal("Result should be a float32")
} else if f != 1.0 {
t.Fatalf(error, a.Result(), 1.0)
}
a.Subtract(2)
if a.Result().(float32) != -1.0 { t.Fatalf(error, a.Result(), -1.0) }
r := FAdder{-1.0, 1.0, 2.0}
for n, v := range a.Adder.(FAdder) {
if r[n] != v { t.Fatalf("Adder %v should be %v", a, r) }
}
}
Thursday, 30 May 13
concurrency
Thursday, 30 May 13
goroutines
concurrent execution stacks
initialised with a closure
scheduled automatically by the runtime
Thursday, 30 May 13
package main
import "fmt"
func main() {
var c chan int
c = make(chan int)
go func() {
for {
fmt.Print(<-c)
}
}()
for {
select {
case c <- 0:
case c <- 1:
}
}
} produces:
01100111010110...
Thursday, 30 May 13
package main
import "fmt"
func main() {
var c chan int
c = make(chan int, 16)
go func() {
for {
fmt.Print(<-c)
}
}()
go func() {
select {
case c <- 0:
case c <- 1:
}
}()
for {}
}
produces:
01100111010110...
Thursday, 30 May 13
package map_reduce
type SignalSource func(status chan bool)
func Wait(s SignalSource) {
done := make(chan bool)
defer close(done)
go s(done)
<-done
}
func WaitCount(count int, s SignalSource) {
done := make(chan bool)
defer close(done)
go s(done)
for i := 0; i < count; i++ {
<- done
}
}
Thursday, 30 May 13
package map_reduce
type Iteration func(k, x interface{})
func (i Iteration) apply(k, v interface{}, c chan bool) {
go func() {
i(k, v)
c <- true
}()
}
Thursday, 30 May 13
package map_reduce
func Each(c interface{}, f Iteration) {
switch c := c.(type) {
case []int: WaitCount(len(c), func(done chan bool) {
for i, v := range c {
f.apply(i, v, done)
}
})
case map[int] int: WaitCount(len(c), func(done chan bool) {
for k, v := range c {
f.apply(k, v, done)
}
})
}
}
Thursday, 30 May 13
package map_reduce
type Results chan interface{}
type Combination func(x, y interface{}) interface{}
func (f Combination) Reduce(c, s interface{}) (r Results) {
r = make(Results)
go func() {
Each(c, func(k, x interface{}) {
s = f(s, x)
})
r <- s
}()
return
}
Thursday, 30 May 13
package map_reduce
type Transformation func(x interface{}) interface{}
func (t Transformation) GetValue(x interface{}) interface{} {
return t(x)
}
Thursday, 30 May 13
func Map(c interface{}, t Transformation) (n interface{}) {
var i Iteration
switch c := c.(type) {
case []int: m := make([]int, len(c))
i = func(k, x interface{}) { m[k] = t.GetValue(x) }
n = m
case map[int] int: m := make(map[int] int)
i = func(k, x interface{}) { m[k] = t.GetValue(x) }
n = m
}
if i != nil {
Wait(func(done chan bool) {
Each(c, i)
done <- true
})
}
return
}
Thursday, 30 May 13
package main
import "fmt"
import . "map_reduce"
func main() {
m := "%v = %v, sum = %vn"
s := []int{0, 1, 2, 3, 4, 5}
sum := func(x, y interface{}) interface{} { return x.(int) + y.(int) }
d := Map(s, func(x interface{}) interface{} { return x.(int) * 2 })
x := <- Combination(sum).Reduce(s, 0)
fmt.Printf("s", s, x)
x = <- Combination(sum).Reduce(d, 0)
fmt.Printf("d", d, x)
}
produces:
s = [0 1 2 3 4 5], sum = 15
c = [0 2 4 6 8 10], sum = 30
Thursday, 30 May 13
software machines
Thursday, 30 May 13
synchronisation
Thursday, 30 May 13
package clock
import "syscall"
type Clock struct {
Period int64
Count chan int64
Control chan bool
active bool
}
Thursday, 30 May 13
package clock
import "syscall"
func (c *Clock) Start() {
if !c.active {
go func() {
c.active = true
for i := int64(0); ; i++ {
select {
case c.active = <- c.Control:
default:
if c.active {
c.Count <- i
}
syscall.Sleep(c.Period)
}
}
}()
}
}
Thursday, 30 May 13
package main
import . "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 <- false
syscall.Sleep(1000000)
println("restarting clock")
c.Control <- true
println("pulse value", <-c.Count, "from clock")
}
Thursday, 30 May 13
OSX 10.6.2 Intel Atom 270 @ 1.6GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 106 from clock
OSX 10.6.7 Intel Core 2 Duo @ 2.4GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 154 from clock
Thursday, 30 May 13
instruction set
Thursday, 30 May 13
operations
CISC
RISC
VLIW
Thursday, 30 May 13
package instructions
import "fmt"
type Operation func(o []int)
type Executable interface {
Opcode() int
Operands() []int
Execute(op Operation)
}
const INVALID_OPCODE = -1
type Program []Executable
func (p Program) Disassemble(a Assembler) {
for _, v := range p {
fmt.Println(a.Disassemble(v))
}
}
Thursday, 30 May 13
package instructions
type Instruction []int
func (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())
}
Thursday, 30 May 13
package instructions
type Assembler struct {
opcodes map[string] int
names map[int] string
}
func NewAssember(names... string) (a Assembler) {
a = Assembler{
opcodes: make(map[string] int),
names:make(map[int] string),
}
a.Define(names...)
return
}
func (a Assembler) Define(names... string) {
for _, name := range names {
a.opcodes[name] = len(a.names)
a.names[len(a.names)] = name
}
}
Thursday, 30 May 13
package instructions
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
}
Thursday, 30 May 13
package instructions
import "fmt"
func (a Assembler) Disassemble(e Executable) (s string) {
if name, ok := a.names[e.Opcode()]; ok {
s = name
if params := e.Operands(); len(params) > 0 {
s = fmt.Sprintf("%vt%v", s, params[0])
for _, v := range params[1:] {
s = fmt.Sprintf("%v, %v", s, v)
}
}
} else {
s = "unknown"
}
return
}
Thursday, 30 May 13
package main
import . "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])
}
}
}
Thursday, 30 May 13
produces:
noop
load 1
store 1, 2
unknown
op = 2 result = 3
Thursday, 30 May 13
processor core
Thursday, 30 May 13
package processor
import . "instructions"
const PROCESSOR_READY = 0
const PROCESSOR_BUSY = 1
const CALL_STACK_UNDERFLOW = 2
const CALL_STACK_OVERFLOW = 4
const ILLEGAL_OPERATION = 8
const INVALID_ADDRESS = 16
type Processor interface {
Run(p []Executable)
}
type Core struct {
Running bool
PC, Flags, Ticks int
CS, M []int
OP Executable "Loaded OpCode"
I chan Executable "Interrupts"
}
Thursday, 30 May 13
package processor
import . "instructions"
func NewCore(CSS, MSS int, I chan Executable) *Core {
return &Core{
CS:make([]int, CSS)},
M: make([]int, MSS),
I: I,
}
}
func (c *Core) Reset() {
c.Running = false
c.Flags = PROCESSOR_READY
}
func (c *Core) Goto(addr int) {
c.PC = addr
}
Thursday, 30 May 13
package processor
func (c *Core) Call(addr int) {
top := len(c.CS)
if top >= cap(c.CS) - 1 { panic(CALL_STACK_OVERFLOW) }
c.CS = c.CS[:top + 1]
c.CS[top] = c.PC
c.PC = addr
}
func (c *Core) TailCall(addr int) {
c.CS[len(c.CS) - 1] = c.PC
c.PC = addr
}
func (c *Core) Return() {
top := len(c.CS)
if top == 0 { panic(CALL_STACK_UNDERFLOW) }
c.PC, c.CS = c.CS[top - 1], c.CS[:top]
}
Thursday, 30 May 13
package processor
import . "instructions"
func (c *Core) Run(p []Executable, dispatchers... func(c *Core)) {
defer func() {
c.Running = false
if x := recover(); x != nil { c.Flags &= x.(int) }
}()
switch {
case c.Running: panic(PROCESSOR_BUSY)
case len(dispatchers) == 0: panic(PROCESSOR_READY)
default:
c.Running = true
c.BusyLoop(dispatchers...)
}
}
func (c *Core) LoadInstruction(program []Executable) {
if c.PC >= len(program) { panic(PROCESSOR_READY) }
c.Executable = program[c.PC]
c.PC++
}
Thursday, 30 May 13
package processor
import . "instructions"
func (c *Core) BusyLoop(p []Executable, dispatchers... func(c *Core)) {
select {
case interrupt <- c.I:
op, pc := c.OP, c.PC
for c.PC = 0; c.Running; c.Ticks++ {
for _, f := range dispatchers { f(c) }
c.LoadInstruction(p)
}
c.OP, c.PC = op, pc
default:
for c.PC = 0; c.Running; c.Ticks++ {
c.LoadInstruction(p)
for _, f := range dispatchers { f(c) }
}
}
}
Thursday, 30 May 13
package processor
import . "instructions"
func (c *Core) RunExclusive(p []Executable, tracers... func(c *Core)) {
defer func() {
c.Running = false
if x := recover(); x != nil { c.Flags &= x.(int) }
}()
if c.Running { panic(PROCESSOR_BUSY) }
case len(dispatchers) == 0: panic(PROCESSOR_READY)
c.Running = true
for c.PC = 0; c.Running; c.Ticks++ {
c.LoadInstruction(p)
for _, f := range tracers { f(c) }
}
}
Thursday, 30 May 13
package main
import . "processor"
import . "instructions"
const( CALL = iota
GOTO
MOVE
RETURN
)
c := NewCore(10, 8, nil)
var dispatcher = func(c *Core) {
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)
}
}
Thursday, 30 May 13
func main() {
p := []Executable{ Instruction{ CALL, 2 },
Instruction{ GOTO, 5 },
Instruction{ MOVE, 2 },
Instruction{ RETURN },
Instruction{ MOVE, -1 } }
c.RunExclusive(p, dispatcher)
fmt.Printf("Instructions Executed: %vnPC = %vn", c.Ticks, c.PC)
if c.Flags | PROCESSOR_READY == PROCESSOR_READY {
fmt.Println("Core Ready")
} else {
fmt.Println("Core Error:", c.Flags)
}
}
produces:
Instructions Executed: 2
PC = 5
Core Ready
Thursday, 30 May 13
accumulator machine
1-operand instructions
data from memory combined with accumulator
result stored in accumulator
Thursday, 30 May 13
package accmachine
import . "processor"
const (
CONSTANT = iota
LOAD_VALUE
STORE_VALUE
ADD
)
type AccMachine struct {
Core
AC int
}
func NewAccMachine(CSSize, MSize int, I chan Executable) *AccMachine {
return &AccMachine{ Core: NewCore(CSSize, MSize, I) }
}
Thursday, 30 May 13
package accmachine
import . "processor"
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)
}
})
}
Thursday, 30 May 13
package main
import . "accmachine"
import . "instructions"
func main() {
a := NewAccMachine(10, 8, nil)
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
Thursday, 30 May 13
stack machine
0-operand instructions
data popped from stack
results pushed on stack
Thursday, 30 May 13
package smachine
import . "processor"
const (
CONSTANT = iota
PUSH_VALUE
POP_VALUE
ADD
)
type StackMachine struct {
Core
DS []int
}
func NewStackM(CSSize, DSSize, MSize int, I chan Executable) *StackMachine {
return &StackMachine{DS: make([]int, 0, DSSize),
Core: NewCore(CSSize, MSize, I) }
}
Thursday, 30 May 13
package smachine
import . "processor"
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) - 1
s.M[addr], s.DS = s.DS[top], s.DS[:top]
}
Thursday, 30 May 13
package smachine
import . "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)
}
})
}
Thursday, 30 May 13
package main
import . "smachine"
import . "instructions"
func main() {
s := NewStackM(10, 10, 8, nil)
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]
Thursday, 30 May 13
register machine
multi-operand instructions
data read from memory into registers
operator combines registers and stores
Thursday, 30 May 13
package rmachine
import . "processor"
const (
CONSTANT = iota
LOAD_VALUE
STORE_VALUE
ADD
)
type RMachine struct {
Core
R []int
}
func NewRMachine(CSSize, RSize, MSize int, I chan Executable) *RMachine {
return &RMachine{ Core: NewCore(CSSize, MSize, I),
R: make([]int, RSize) }
}
Thursday, 30 May 13
package rmachine
import . "processor"
func (r *RMachine) 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)
}
})
}
Thursday, 30 May 13
package main
import . "rmachine"
import . "instructions"
func main() {
r := NewRMachine(10, 10, 8, nil)
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]
Thursday, 30 May 13
vector machine
multi-operand instructions
data vectors read from memory into registers
operations combine registers
Thursday, 30 May 13
package vmachine
import . "processor"
const (
CONSTANT = iota
LOAD_VALUE
STORE_VALUE
ADD
)
type VMachine struct {
Core
R [][]int
}
func NewVMachine(CSSize, RSize, MSize int, I chan Executable) *VMachine {
return &VectorMachine{ Core: NewCore(CSSize, MSize),
make([][]int, RSize) }
}
Thursday, 30 May 13
package vmachine
import . "processor"
func (v *VMachine) Load(r int, m []int) {
v.R[r] = make([]int, len(m))
copy(v.R[r], m)
}
Thursday, 30 May 13
package vmachine
import . "processor"
func (v *VMachine) Run(program []Executable) {
v.RunExclusive(program, func() {
switch v.Opcode() {
case CONSTANT: v.Execute(func(o []int) { v.Load(o[0], o[1:]) })
case STORE_VALUE: v.Execute(func(o []int) { copy(v.M[o[0]:], v.R[o[1]]) })
case LOAD_VALUE: v.Execute(func(o []int) {
v.Load(o[0], v.M[o[1]:o[1] + o[2]])
})
case ADD: v.Execute(func(o []int) {
a, b := v.R[o[0]], v.R[o[1]]
count := len(a)
if len(b) < len(a) { count := len(b) }
for ; count > 0; count-- { a[i] += b[i] }
})
default: panic(ILLEGAL_OPERATION)
}
})
}
Thursday, 30 May 13
package main
import . "vmachine"
import . "instructions"
func main() {
r := NewVMachine(10, 10, 8, nil)
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] [] [] [] [] [] [] []]
Thursday, 30 May 13
related projects
gospeed
raw - slices - lists - chains - sexp
wendigo
Thursday, 30 May 13
related projects
gospeed
typelib
wendigo
Thursday, 30 May 13
finding out more
http://golang.org/
twitter://#golang
http://slides.games-with-brains.net/
http://github.com/feyeleanor/
wikipedia or google
Thursday, 30 May 13

GoLightly - a customisable virtual machine written in Go

  • 1.
    golightly a customisable virtualmachine written in Go Eleanor McHugh http://slides.games-with-brains.net/ Thursday, 30 May 13
  • 2.
    portrait of anartist... physics major embedded controllers software reliability dynamic languages network scaling questionable taste in music http://github.com/feyeleanor Eleanor McHugh Thursday, 30 May 13
  • 3.
    go... small, safety-conscious systemslanguage concurrency, closures & garbage collection consistently fast compilation Thursday, 30 May 13
  • 4.
    ...lightly agnostic virtual machinenetworks application performance matters non-viral open source license Thursday, 30 May 13
  • 5.
    agnostic no blessed programminglanguages flexible platform abstractions write once, run everywhere it matters Thursday, 30 May 13
  • 6.
    heterogeneous a system comprisesmany components components may differ in purpose and design but they cooperate to solve problems Thursday, 30 May 13
  • 7.
    networks machines cooperate bysending messages machine states can be serialised as messages messages transcend process and host boundaries Thursday, 30 May 13
  • 8.
    inspiration hardware design sensor andcontrol networks field-programmable gate arrays Thursday, 30 May 13
  • 9.
    virtual machine emulate anexisting system simulate an imagined system have dynamic control of system state Thursday, 30 May 13
  • 10.
    stack-based lambda calculusprocessor threaded value cells in a von neumann memory all operations are expressions are values lisp Thursday, 30 May 13
  • 11.
    stack-based processor threaded worddefinitions in a von neumann memory words thread primitives, word pointers and data forth Thursday, 30 May 13
  • 12.
    stack-based processor withinstruction set harvard memory separates code and data class loaders convert bytecode to machine state jvm Thursday, 30 May 13
  • 13.
    linux kernel hypervisor intelX86 virtualisation with hardware execution QEMU virtual machine runs in user space kvm Thursday, 30 May 13
  • 14.
  • 15.
    package main import "fmt" const( HELLOstring = "hello" WORLD string = "world" ) func main() { fmt.Println(HELLO, WORLD) } Thursday, 30 May 13
  • 16.
  • 17.
    package Integer type Intint func (i *Int) Add(x int) { *i += Int(x) } Thursday, 30 May 13
  • 18.
    package Integer type Buffer[]Int func (b Buffer) Eq(o Buffer) (r bool) { if len(b) == len(o) { for i := len(b) - 1; i > 0; i-- { if b[i] != o[i] { return } } r = true } return } func (b Buffer) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b Buffer) Clone() Buffer { s := make(Buffer, len(b)) copy(s, b) return s } 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) } Thursday, 30 May 13
  • 19.
    package main import "Integer" funcmain() { 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[:2] = {", b[0], ",", b[1], "}") } produces: b[0:2] = { 6, 4 } Thursday, 30 May 13
  • 20.
    package Integer import "testing" funcTestSwap(t *testing.T) { i := Buffer{0, 1, 2, 3, 4, 5} b := i.Clone() b.Swap(1, 2) if !b[1:3].Eq(Buffer{2, 1}) { t.Fatalf("b[:5] = %v", b) } } func TestMove(t *testing.T) { i := Buffer{0, 1, 2, 3, 4, 5} b := i.Clone() b.Move(3, 2) if !b.Eq(Buffer{3, 4, 0, 1, 2, 5}) { t.Fatalf("b[:5] = %v", b) } } func TestAdd(t *testing.T) { i := Buffer{0, 1, 2, 3, 4, 5} b := i.Clone() b[0].Add(3) if b[0] != i[0] + 3 { t.Fatalf("b[:5] = %v", b) } } Thursday, 30 May 13
  • 21.
  • 22.
    package Vector import ."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] } Thursday, 30 May 13
  • 23.
    package Integer import "testing" funcTestVectorSwap(t *testing.T) { i := Vector{Buffer{0, 1, 2, 3, 4, 5}} v := i.Clone() v.Swap(1, 2) r := Vector{Buffer{0, 2, 1, 3, 4, 5}} switch { case !v.Match(&r): fallthrough case !v.Buffer.Match(r.Buffer): t.Fatalf("b[:5] = %v", v) } } Thursday, 30 May 13
  • 24.
    package integer import "testing" funcBenchmarkVectorClone6(b *testing.B) { v := Vector{Buffer{0, 1, 2, 3, 4, 5}} for i := 0; i < b.N; i++ { _ = v.Clone() } } func BenchmarkVectorSwap(b *testing.B) { b.StopTimer() v := Vector{Buffer{0, 1, 2, 3, 4, 5}} b.StartTimer() for i := 0; i < b.N; i++ { v.Swap(1, 2) } } Thursday, 30 May 13
  • 25.
    $ go test-test.bench="Benchmark" PASS integer.BenchmarkVectorSwap 200000000 8 ns/op integer.BenchmarkVectorClone6 10000000 300 ns/op Thursday, 30 May 13
  • 26.
  • 27.
    package adder type Adderinterface { Add(j int) Subtract(j int) Result() interface{} } type Calculator interface { Adder Reset() } type AddingMachine struct { Memory interface{} Adder } Thursday, 30 May 13
  • 28.
    package adder type IAdder[]int func (i IAdder) Add(j int) { i[0] += i[j] } func (i IAdder) Subtract(j int) { i[0] -= i[j] } func (i IAdder) Result() interface{} { return i[0] } func (i IAdder) Reset() { i[0] = *new(int) } Thursday, 30 May 13
  • 29.
    package adder import "testing" funcTestIAdder(t *testing.T) { error := "Result %v != %v" i := IAdder{0, 1, 2} i.Add(1) if i.Result().(int) != 1 { t.Fatalf(error, i.Result(), 1) } i.Subtract(2) if i.Result().(int) != -1 { t.Fatalf(error, i.Result()), -1 } var r Calculator = IAdder{-1, 1, 2} for n, v := range r.(IAdder) { if i[n] != v { t.Fatalf("Adder %v should be %v", i, r) } } r.Reset() if r.Result().(int) != *new(int) { t.Fatalf(error, r.Result(), *new(int)) } } Thursday, 30 May 13
  • 30.
    package adder import "testing" funcTestIAdder(t *testing.T) { error := "Result %v != %v" i := IAdder{0, 1, 2} i.Add(1) if i.Result() != 1 { t.Fatalf(error, i.Result(), 1) } i.Subtract(2) if i.Result() != -1 { t.Fatalf(error, i.Result()), -1 } var r Calculator = IAdder{-1, 1, 2} for n, v := range r.(IAdder) { if i[n] != v { t.Fatalf("Adder %v should be %v", i, r) } } r.Reset() if r.Result() != *new(int) { t.Fatalf(error, r.Result(), *new(int)) } } Thursday, 30 May 13
  • 31.
    package adder type FAdder[]float32 func (f FAdder) Add(j int) { f[0] += f[j] } func (f FAdder) Subtract(j int) { f[0] -= f[j] } func (f FAdder) Result() interface{} { return f[0] } func (f FAdder) Reset() { f[0] = *new(float32) } Thursday, 30 May 13
  • 32.
    package adder import "testing" funcTestFAdder(t *testing.T) { error := "Result %v != %v" f := FAdder{0.0, 1.0, 2.0} f.Add(1) if f.Result() != 1.0 { t.Fatalf(error, f.Result(), 1.0) } f.Subtract(2) if i.Result() != -1.0 { t.Fatalf(error, i.Result()), -1.0 } var r Calculator = FAdder{-1.0, 1.0, 2.0} for n, v := range r.(FAdder) { if f[n] != v { t.Fatalf("Adder %v should be %v", f, r) } } r.Reset() if r.Result() != *new(float32) { t.Fatalf(error, r.Result(), *new(float32)) } } Thursday, 30 May 13
  • 33.
    package adder import "testing" funcTestAddingMachine(t *testing.T) { error := "Result %v != %v" a := &AddingMachine{ Adder: FAdder{0.0, 1.0, 2.0} } a.Add(1) if f, ok := a.Result().(float32); !ok { t.Fatal("Result should be a float32") } else if f != 1.0 { t.Fatalf(error, a.Result(), 1.0) } a.Subtract(2) if a.Result().(float32) != -1.0 { t.Fatalf(error, a.Result(), -1.0) } r := FAdder{-1.0, 1.0, 2.0} for n, v := range a.Adder.(FAdder) { if r[n] != v { t.Fatalf("Adder %v should be %v", a, r) } } } Thursday, 30 May 13
  • 34.
  • 35.
    goroutines concurrent execution stacks initialisedwith a closure scheduled automatically by the runtime Thursday, 30 May 13
  • 36.
    package main import "fmt" funcmain() { var c chan int c = make(chan int) go func() { for { fmt.Print(<-c) } }() for { select { case c <- 0: case c <- 1: } } } produces: 01100111010110... Thursday, 30 May 13
  • 37.
    package main import "fmt" funcmain() { var c chan int c = make(chan int, 16) go func() { for { fmt.Print(<-c) } }() go func() { select { case c <- 0: case c <- 1: } }() for {} } produces: 01100111010110... Thursday, 30 May 13
  • 38.
    package map_reduce type SignalSourcefunc(status chan bool) func Wait(s SignalSource) { done := make(chan bool) defer close(done) go s(done) <-done } func WaitCount(count int, s SignalSource) { done := make(chan bool) defer close(done) go s(done) for i := 0; i < count; i++ { <- done } } Thursday, 30 May 13
  • 39.
    package map_reduce type Iterationfunc(k, x interface{}) func (i Iteration) apply(k, v interface{}, c chan bool) { go func() { i(k, v) c <- true }() } Thursday, 30 May 13
  • 40.
    package map_reduce func Each(cinterface{}, f Iteration) { switch c := c.(type) { case []int: WaitCount(len(c), func(done chan bool) { for i, v := range c { f.apply(i, v, done) } }) case map[int] int: WaitCount(len(c), func(done chan bool) { for k, v := range c { f.apply(k, v, done) } }) } } Thursday, 30 May 13
  • 41.
    package map_reduce type Resultschan interface{} type Combination func(x, y interface{}) interface{} func (f Combination) Reduce(c, s interface{}) (r Results) { r = make(Results) go func() { Each(c, func(k, x interface{}) { s = f(s, x) }) r <- s }() return } Thursday, 30 May 13
  • 42.
    package map_reduce type Transformationfunc(x interface{}) interface{} func (t Transformation) GetValue(x interface{}) interface{} { return t(x) } Thursday, 30 May 13
  • 43.
    func Map(c interface{},t Transformation) (n interface{}) { var i Iteration switch c := c.(type) { case []int: m := make([]int, len(c)) i = func(k, x interface{}) { m[k] = t.GetValue(x) } n = m case map[int] int: m := make(map[int] int) i = func(k, x interface{}) { m[k] = t.GetValue(x) } n = m } if i != nil { Wait(func(done chan bool) { Each(c, i) done <- true }) } return } Thursday, 30 May 13
  • 44.
    package main import "fmt" import. "map_reduce" func main() { m := "%v = %v, sum = %vn" s := []int{0, 1, 2, 3, 4, 5} sum := func(x, y interface{}) interface{} { return x.(int) + y.(int) } d := Map(s, func(x interface{}) interface{} { return x.(int) * 2 }) x := <- Combination(sum).Reduce(s, 0) fmt.Printf("s", s, x) x = <- Combination(sum).Reduce(d, 0) fmt.Printf("d", d, x) } produces: s = [0 1 2 3 4 5], sum = 15 c = [0 2 4 6 8 10], sum = 30 Thursday, 30 May 13
  • 45.
  • 46.
  • 47.
    package clock import "syscall" typeClock struct { Period int64 Count chan int64 Control chan bool active bool } Thursday, 30 May 13
  • 48.
    package clock import "syscall" func(c *Clock) Start() { if !c.active { go func() { c.active = true for i := int64(0); ; i++ { select { case c.active = <- c.Control: default: if c.active { c.Count <- i } syscall.Sleep(c.Period) } } }() } } Thursday, 30 May 13
  • 49.
    package main import ."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 <- false syscall.Sleep(1000000) println("restarting clock") c.Control <- true println("pulse value", <-c.Count, "from clock") } Thursday, 30 May 13
  • 50.
    OSX 10.6.2 IntelAtom 270 @ 1.6GHz: pulse value 0 from clock pulse value 1 from clock pulse value 2 from clock disabling clock restarting clock pulse value 106 from clock OSX 10.6.7 Intel Core 2 Duo @ 2.4GHz: pulse value 0 from clock pulse value 1 from clock pulse value 2 from clock disabling clock restarting clock pulse value 154 from clock Thursday, 30 May 13
  • 51.
  • 52.
  • 53.
    package instructions import "fmt" typeOperation func(o []int) type Executable interface { Opcode() int Operands() []int Execute(op Operation) } const INVALID_OPCODE = -1 type Program []Executable func (p Program) Disassemble(a Assembler) { for _, v := range p { fmt.Println(a.Disassemble(v)) } } Thursday, 30 May 13
  • 54.
    package instructions type Instruction[]int func (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()) } Thursday, 30 May 13
  • 55.
    package instructions type Assemblerstruct { opcodes map[string] int names map[int] string } func NewAssember(names... string) (a Assembler) { a = Assembler{ opcodes: make(map[string] int), names:make(map[int] string), } a.Define(names...) return } func (a Assembler) Define(names... string) { for _, name := range names { a.opcodes[name] = len(a.names) a.names[len(a.names)] = name } } Thursday, 30 May 13
  • 56.
    package instructions func (aAssembler) 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 } Thursday, 30 May 13
  • 57.
    package instructions import "fmt" func(a Assembler) Disassemble(e Executable) (s string) { if name, ok := a.names[e.Opcode()]; ok { s = name if params := e.Operands(); len(params) > 0 { s = fmt.Sprintf("%vt%v", s, params[0]) for _, v := range params[1:] { s = fmt.Sprintf("%v, %v", s, v) } } } else { s = "unknown" } return } Thursday, 30 May 13
  • 58.
    package main import ."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]) } } } Thursday, 30 May 13
  • 59.
    produces: noop load 1 store 1,2 unknown op = 2 result = 3 Thursday, 30 May 13
  • 60.
  • 61.
    package processor import ."instructions" const PROCESSOR_READY = 0 const PROCESSOR_BUSY = 1 const CALL_STACK_UNDERFLOW = 2 const CALL_STACK_OVERFLOW = 4 const ILLEGAL_OPERATION = 8 const INVALID_ADDRESS = 16 type Processor interface { Run(p []Executable) } type Core struct { Running bool PC, Flags, Ticks int CS, M []int OP Executable "Loaded OpCode" I chan Executable "Interrupts" } Thursday, 30 May 13
  • 62.
    package processor import ."instructions" func NewCore(CSS, MSS int, I chan Executable) *Core { return &Core{ CS:make([]int, CSS)}, M: make([]int, MSS), I: I, } } func (c *Core) Reset() { c.Running = false c.Flags = PROCESSOR_READY } func (c *Core) Goto(addr int) { c.PC = addr } Thursday, 30 May 13
  • 63.
    package processor func (c*Core) Call(addr int) { top := len(c.CS) if top >= cap(c.CS) - 1 { panic(CALL_STACK_OVERFLOW) } c.CS = c.CS[:top + 1] c.CS[top] = c.PC c.PC = addr } func (c *Core) TailCall(addr int) { c.CS[len(c.CS) - 1] = c.PC c.PC = addr } func (c *Core) Return() { top := len(c.CS) if top == 0 { panic(CALL_STACK_UNDERFLOW) } c.PC, c.CS = c.CS[top - 1], c.CS[:top] } Thursday, 30 May 13
  • 64.
    package processor import ."instructions" func (c *Core) Run(p []Executable, dispatchers... func(c *Core)) { defer func() { c.Running = false if x := recover(); x != nil { c.Flags &= x.(int) } }() switch { case c.Running: panic(PROCESSOR_BUSY) case len(dispatchers) == 0: panic(PROCESSOR_READY) default: c.Running = true c.BusyLoop(dispatchers...) } } func (c *Core) LoadInstruction(program []Executable) { if c.PC >= len(program) { panic(PROCESSOR_READY) } c.Executable = program[c.PC] c.PC++ } Thursday, 30 May 13
  • 65.
    package processor import ."instructions" func (c *Core) BusyLoop(p []Executable, dispatchers... func(c *Core)) { select { case interrupt <- c.I: op, pc := c.OP, c.PC for c.PC = 0; c.Running; c.Ticks++ { for _, f := range dispatchers { f(c) } c.LoadInstruction(p) } c.OP, c.PC = op, pc default: for c.PC = 0; c.Running; c.Ticks++ { c.LoadInstruction(p) for _, f := range dispatchers { f(c) } } } } Thursday, 30 May 13
  • 66.
    package processor import ."instructions" func (c *Core) RunExclusive(p []Executable, tracers... func(c *Core)) { defer func() { c.Running = false if x := recover(); x != nil { c.Flags &= x.(int) } }() if c.Running { panic(PROCESSOR_BUSY) } case len(dispatchers) == 0: panic(PROCESSOR_READY) c.Running = true for c.PC = 0; c.Running; c.Ticks++ { c.LoadInstruction(p) for _, f := range tracers { f(c) } } } Thursday, 30 May 13
  • 67.
    package main import ."processor" import . "instructions" const( CALL = iota GOTO MOVE RETURN ) c := NewCore(10, 8, nil) var dispatcher = func(c *Core) { 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) } } Thursday, 30 May 13
  • 68.
    func main() { p:= []Executable{ Instruction{ CALL, 2 }, Instruction{ GOTO, 5 }, Instruction{ MOVE, 2 }, Instruction{ RETURN }, Instruction{ MOVE, -1 } } c.RunExclusive(p, dispatcher) fmt.Printf("Instructions Executed: %vnPC = %vn", c.Ticks, c.PC) if c.Flags | PROCESSOR_READY == PROCESSOR_READY { fmt.Println("Core Ready") } else { fmt.Println("Core Error:", c.Flags) } } produces: Instructions Executed: 2 PC = 5 Core Ready Thursday, 30 May 13
  • 69.
    accumulator machine 1-operand instructions datafrom memory combined with accumulator result stored in accumulator Thursday, 30 May 13
  • 70.
    package accmachine import ."processor" const ( CONSTANT = iota LOAD_VALUE STORE_VALUE ADD ) type AccMachine struct { Core AC int } func NewAccMachine(CSSize, MSize int, I chan Executable) *AccMachine { return &AccMachine{ Core: NewCore(CSSize, MSize, I) } } Thursday, 30 May 13
  • 71.
    package accmachine import ."processor" 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) } }) } Thursday, 30 May 13
  • 72.
    package main import ."accmachine" import . "instructions" func main() { a := NewAccMachine(10, 8, nil) 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 Thursday, 30 May 13
  • 73.
    stack machine 0-operand instructions datapopped from stack results pushed on stack Thursday, 30 May 13
  • 74.
    package smachine import ."processor" const ( CONSTANT = iota PUSH_VALUE POP_VALUE ADD ) type StackMachine struct { Core DS []int } func NewStackM(CSSize, DSSize, MSize int, I chan Executable) *StackMachine { return &StackMachine{DS: make([]int, 0, DSSize), Core: NewCore(CSSize, MSize, I) } } Thursday, 30 May 13
  • 75.
    package smachine import ."processor" 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) - 1 s.M[addr], s.DS = s.DS[top], s.DS[:top] } Thursday, 30 May 13
  • 76.
    package smachine import ."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) } }) } Thursday, 30 May 13
  • 77.
    package main import ."smachine" import . "instructions" func main() { s := NewStackM(10, 10, 8, nil) 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] Thursday, 30 May 13
  • 78.
    register machine multi-operand instructions dataread from memory into registers operator combines registers and stores Thursday, 30 May 13
  • 79.
    package rmachine import ."processor" const ( CONSTANT = iota LOAD_VALUE STORE_VALUE ADD ) type RMachine struct { Core R []int } func NewRMachine(CSSize, RSize, MSize int, I chan Executable) *RMachine { return &RMachine{ Core: NewCore(CSSize, MSize, I), R: make([]int, RSize) } } Thursday, 30 May 13
  • 80.
    package rmachine import ."processor" func (r *RMachine) 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) } }) } Thursday, 30 May 13
  • 81.
    package main import ."rmachine" import . "instructions" func main() { r := NewRMachine(10, 10, 8, nil) 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] Thursday, 30 May 13
  • 82.
    vector machine multi-operand instructions datavectors read from memory into registers operations combine registers Thursday, 30 May 13
  • 83.
    package vmachine import ."processor" const ( CONSTANT = iota LOAD_VALUE STORE_VALUE ADD ) type VMachine struct { Core R [][]int } func NewVMachine(CSSize, RSize, MSize int, I chan Executable) *VMachine { return &VectorMachine{ Core: NewCore(CSSize, MSize), make([][]int, RSize) } } Thursday, 30 May 13
  • 84.
    package vmachine import ."processor" func (v *VMachine) Load(r int, m []int) { v.R[r] = make([]int, len(m)) copy(v.R[r], m) } Thursday, 30 May 13
  • 85.
    package vmachine import ."processor" func (v *VMachine) Run(program []Executable) { v.RunExclusive(program, func() { switch v.Opcode() { case CONSTANT: v.Execute(func(o []int) { v.Load(o[0], o[1:]) }) case STORE_VALUE: v.Execute(func(o []int) { copy(v.M[o[0]:], v.R[o[1]]) }) case LOAD_VALUE: v.Execute(func(o []int) { v.Load(o[0], v.M[o[1]:o[1] + o[2]]) }) case ADD: v.Execute(func(o []int) { a, b := v.R[o[0]], v.R[o[1]] count := len(a) if len(b) < len(a) { count := len(b) } for ; count > 0; count-- { a[i] += b[i] } }) default: panic(ILLEGAL_OPERATION) } }) } Thursday, 30 May 13
  • 86.
    package main import ."vmachine" import . "instructions" func main() { r := NewVMachine(10, 10, 8, nil) 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] [] [] [] [] [] [] []] Thursday, 30 May 13
  • 87.
    related projects gospeed raw -slices - lists - chains - sexp wendigo Thursday, 30 May 13
  • 88.
  • 89.