Contenu connexe Similaire à GoLightly: Building VM-Based Language Runtimes with Google Go (20) Plus de Eleanor McHugh (20) GoLightly: Building VM-Based Language Runtimes with Google Go2. portrait of an artist...
physics major
http:/ feyele
embedded controllers
/
software reliability
dynamic languages
network scaling or.tel an
questionable taste in music Elean or McHugh
3. Languages Project
? fluid-dynamics simulation
? cockpit autopilot controller
? paramilitary cockpit C4I
? broadcast automation
? encrypted RDBM
? Unix kernel scripting
4. Language Project
ICON fluid-dynamics simulation
ASM cockpit autopilot controller
VB5 paramilitary cockpit C4I
VB5, ASM, K&R C broadcast automation
RUBY, DNS encrypted RDBM
RUBY Unix kernel scripting
8. Elric sent his mind into twisting tunnels of logic,
across endless plains of ideas, through mountains
of symbolism and endless universes of alternate
truths; he sent his mind out further and further
and as it went he sent with it the words which
issued from his writhing lips -- words that few
of his contemporaries would understand...
- Elric of Melniboné, Michael Moorcock
10. go...
a systems language by google
productivity, performance, concurrency
lighter than Java, safer than C
11. ...lightly
clean abstractions
geared to performance
non-viral open source license
12. inspiration
processor design
sensor and control networks
field-programmable gate arrays
13. perspiration
iterative empirical development
explore -> implement -> test -> benchmark
evolve towards elegance
14. principles
decoupling improves scalability
coherence simplifies organisation
optimisations are application specific
15. agnostic
no blessed programming languages
flexible platform abstractions
write once, run everywhere it matters
16. heterogeneous
a system comprises many components
components may differ in purpose and design
but they cooperate to solve problems
17. virtualisation
design discrete Turing machines
implement these machines in software
compile programs to run on them
18. networks
machines cooperate by sending messages
machine states can be serialised as messages
messages transcend process and host boundaries
19. 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
21. an elegant language
a statically-typed compiled language
object-oriented
static type declaration
dynamic type inference
garbage collection
concurrency via communication (CSP)
25. boolean, numeric, array
value
structure, interface
reference pointer, slice, string, map, channel
function function, method, closure
27. underlying method
type set
expressed
type
embedded
types
30. package Integer func (b Buffer) Clone() Buffer {
s := make(Buffer, len(b))
type Buffer []Int copy(s, b)
return s
func (b Buffer) Eq(o Buffer) (r bool) { }
if len(b) == len(o) {
for i := len(b) - 1; i > 0; i-- { func (b Buffer) Move(i, n int) {
if b[i] != o[i] { if n > len(b) - i {
return n = len(b) - i
} }
} segment_to_move := b[:i].Clone()
r = true copy(b, b[i:i + n])
} copy(b[n:i + n],
return segment_to_move)
} }
func (b Buffer) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
31. 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[0:2] = {”, b[0], “,”, b[1], “}”)
}
produces:
b[0:2] = { 6, 4 }
34. package Integer func (b Buffer) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
type Int int }
func (i *Int) Add(x int) {
*i += Int(x) func (b Buffer) Clone() Buffer {
} s := make(Buffer, len(b))
copy(s, b)
type Buffer []Int return s
func (b Buffer) Eq(o Buffer) (r bool) { }
if len(b) == len(o) {
for i := len(b) - 1; i > 0; i-- { func (b Buffer) Move(i, n int) {
if b[i] != o[i] { if n > len(b) - i {
return n = len(b) - i
} }
} segment_to_move := b[:i].Clone()
r = true copy(b, b[i:i + n])
} copy(b[n:i + n],
return segment_to_move)
} }
35. package Integer func TestAdd(t *testing.T) {
import “testing” i := Buffer{0, 1, 2, 3, 4, 5}
b := i.Clone()
func TestSwap(t *testing.T) { b[0].Add(3)
i := Buffer{0, 1, 2, 3, 4, 5} if b[0] != i[0] + 3 {
b := i.Clone() t.Fatalf("b[0:5] = %v", b)
b.Swap(1, 2) }
if !b[1:3].Eq(Buffer{2, 1}) { }
t.Fatalf("b[0: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[0:5] = %v", b)
}
}
37. 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]
}
39. 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[0:5] = %v", v)
}
}
41. 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)
}
}
42. $ gotest -bench="Benchmark"
rm -f _test/scripts.a
6g -o _gotest_.6 integer.go vector.go nominal_typing_test.go
embedded_typing_benchmark_test.go embedded_typing_test.go
rm -f _test/scripts.a
gopack grc _test/scripts.a _gotest_.6
PASS
integer.BenchmarkVectorSwap! 200000000! 8 ns/op
integer.BenchmarkVectorClone6! 10000000! 300 ns/op
44. package adder
type Adder interface {
Add(j int)
Subtract(j int)
Result() interface{}
}
type Calculator interface {
Adder
Reset()
}
type AddingMachine struct {
Memory interface{}
Adder
}
45. 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)
}
46. 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))
}
}
47. 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)
}
48. 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().(float32) != 1.0 { t.Fatalf(error, f.Result(), 1.0) }
f.Subtract(2)
if i.Result().(float32) != -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().(float32) != *new(float32) {
t.Fatalf(error, r.Result(), *new(float32))
}
}
49. 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) }
}
}
50. package generalise
import "reflect"
func Allocate(i interface{}, limit... int) (n interface{}) {
v := reflect.NewValue(i)
switch v := v.(type) {
case *reflect.SliceValue:
l := v.Cap()
if len(limit) > 0 { l = limit[0] }
t := v.Type().(*reflect.SliceType)
n = reflect.MakeSlice(t, l, l).Interface()
case *reflect.MapValue:
t := v.Type().(*reflect.MapType)
n = reflect.MakeMap(t).Interface()
}
return
}
51. package generalise
import . "reflect"
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)
Copy(temp, destination)
Copy(destination, source)
Copy(source, temp)
} else {
panic(i)
}
}
52. package generalise
import . "reflect"
func Duplicate(i interface{}) (clone interface{}) {
if clone = Allocate(i); clone != nil {
switch clone := NewValue(clone).(type) {
case *SliceValue:
s := NewValue(i).(*SliceValue)
Copy(clone, s)
case *MapValue:
m := NewValue(i).(*MapValue)
for _, k := range m.Keys() {
clone.SetElem(k, m.Elem(k))
}
}
}
return
}
54. func TestAllocate(t *testing.T) {
var s2 []int
s1 := []int{0, 1, 2}
m := map[int] int{1: 1, 2: 2, 3: 3}
switch {
case throwsPanic(func() { s2 = Allocate(s1, 1).([]int) }):
t.Fatal("Unable to allocate new slice")
case len(s2) != 1: fallthrough
case cap(s2) != 1:
t.Fatal("New slice should be %v not %v", make([]int, 0, 1), s2)
case throwsPanic(func() { Allocate(m) }):
t.Fatal("Unable to allocate new map")
}
}
55. func TestDuplicate(t *testing.T) {
error := "Duplicating %v produced %v"
s1 := []int{0, 1, 2}
var s2 []int
m1 := map[int]int{1: 1, 2: 2, 3: 3}
var m2 map[int]int
switch {
case throwsPanic(func() { s2 = Duplicate(s1).([]int) }):
t.Fatalf("Unable to duplicate slice %vn", s1)
case len(s1) != len(s2): fallthrough
case cap(s1) != cap(s2): fallthrough
case s1[0] != s2[0] || s1[1] != s2[1] || s1[2] != s2[2]:
t.Fatalf(error, s1, s2)
case throwsPanic(func() { m2 = Duplicate(m1).(map[int]int) }):
t.Fatalf("Unable to duplicate map %vn", m1)
case len(m1) != len(m2): fallthrough
case m1[1] != m2[1] || m1[2] != m2[2] || m1[3] != m2[3]:
t.Fatalf(error, m1, m2)
}
}
56. func TestSwapSlices(t *testing.T) {
error := "%v became %v but should be %v"
s1 := []int{0, 1, 2, 3, 4, 5}
s2 := Duplicate(s1).([]int)
r := []int{3, 4, 5, 0, 1, 2}
m := map[int] int{1: 1, 2: 2}
switch {
case !throwsPanic(func() { SwapSlices(m, 0, 3, 3) }):
t.Fatalf("Successfully swapped slices %vn", m)
case throwsPanic(func() { SwapSlices(s2, 0, 3, 3) }):
t.Fatalf("Unable to swap slices %vn", s2)
case len(s2) != len(r):
t.Fatalf(error, s1, s2, r)
default:
for i, v := range s2 {
if r[i] != v { t.Fatalf(error, s1, s2, r) }
}
}
}
58. package raw
import . "reflect"
import "unsafe"
var _BYTE_SLICE! Type
var _STRING! ! Type
!
func init() {
_BYTE_SLICE = Typeof([]byte(nil))
_STRING = Typeof("")
}
type MemoryBlock interface {
ByteSlice() []byte
}
59. func SliceHeader(i interface{}) (Header *SliceHeader, Size, Align int) {
value := NewValue(i)
switch value := value.(type) {
case *SliceValue:
Header = (*SliceHeader)(unsafe.Pointer(value.UnsafeAddr()))
t := value.Type().(*SliceType).Elem()
Size = int(t.Size())
Align = t.Align()
case nil:! ! ! ! ! panic(i)
!
case *InterfaceValue:! Header, Size, Align = SliceHeader(value.Elem())
case *PtrValue:! ! ! Header, Size, Align = SliceHeader(value.Elem())
}
return
}
60. func Scale(oldHeader *SliceHeader, oldESize, newESize int) (h *SliceHeader) {
! if oldHeader != nil {
! ! s := float64(oldESize) / float64(newESize)
! ! h = &SliceHeader{ Data: oldHeader.Data }
! ! h.Len = int(float64(oldHeader.Len) * s)
! ! h.Cap = int(float64(oldHeader.Cap) * s)
! }
! return
}
61. func ByteSlice(i interface{}) []byte {
switch b := i.(type) {
case []byte:! ! ! ! ! ! return b
case MemoryBlock:! ! ! ! return b.ByteSlice()
case nil:! ! ! ! ! ! ! return []byte{}
!
}
var header *SliceHeader
value := NewValue(i)
switch value := value.(type) {
case nil:! ! ! ! ! ! ! return []byte{}
!
case Type:! ! ! ! ! ! ! panic(i)
case *MapValue:! ! ! ! ! panic(i)
case *ChanValue:! ! ! ! ! panic(i)
case *InterfaceValue:! ! ! return ByteSlice(value.Elem())
case *PtrValue:
if value := value.Elem(); value == nil {
return ByteSlice(nil)
} else {
size := int(value.Type().Size())
header = &SliceHeader{ value.UnsafeAddr(), size, size }
}
62. case *SliceValue:
h, s, _ := SliceHeader(i)
header = Scale(h, s, 1)
case *StringValue:
s := value.Get()
h := *(*StringHeader)(unsafe.Pointer(&s))
header = &SliceHeader{ h.Data, h.Len, h.Len }
default:
size := int(value.Type().Size())
header = &SliceHeader{ value.UnsafeAddr(), size, size }
}
return unsafe.Unreflect(_BYTE_SLICE, unsafe.Pointer(header)).([]byte)
}
65. 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...
66. 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...
67. package generalise
import . "reflect"
type SignalSource func(status chan bool)
func (s SignalSource) Wait() {
done := make(chan bool)
defer close(done)
go s(done)
<-done
}
func (s SignalSource) WaitAll(count int) {
done := make(chan bool)
defer close(done)
go s(done)
for i := 0; i < count; i++ {
<- done
}
}
69. package generalise
import . "reflect"
func (f Iteration) Each(c interface{}) {
switch c := NewValue(c).(type) {
case *SliceValue:! ! SignalSource(func(done chan bool) {
! ! ! ! ! ! for i := 0; i < c.Len(); i++ {
! ! ! ! ! ! ! f.apply(i, c.Elem(i).Interface(), done)
! ! ! ! ! ! }
! ! ! ! ! ! }).WaitAll(c.Len())
case *MapValue:! ! SignalSource(func(done chan bool) {
! ! ! ! ! ! for _, k := range c.Keys() {
! ! ! ! ! ! ! f.apply(k, c.Elem(k).Interface(), done)
! ! ! ! ! ! }
! ! ! ! ! ! }).WaitAll(c.Len())
}
}
70. package generalise
import . "reflect"
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() {
Iteration(func(k, x interface{}) {
s = f(s, x)
}).Each(c)
r <- s
}()
return
}
71. package generalise
import . "reflect"
type Transformation func(x interface{}) interface{}
func (t Transformation) GetValue(x interface{}) Value {
return NewValue(t(x))
}
72. 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
! ! ! ! ! ! }).Wait()
! ! ! ! ! ! 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
! ! ! ! ! ! }).Wait()
! ! ! ! ! ! return n.Interface()
}
return Duplicate(c)
}
73. func (t Transformation) Map(c interface{}) interface{} {
var i Iteration
n := NewValue(Allocate(c))
switch n := n.(type) {
case *SliceValue:! ! i = Iteration(func(k, x interface{}) {
! ! ! ! ! ! ! ! n.Elem(k.(int)).SetValue(t.GetValue(x))
! ! ! ! ! ! })
case *MapValue:! ! i = Iteration(func(k, x interface{}) {
! ! ! ! ! ! ! ! n.SetElem(NewValue(k), t.GetValue(x))
! ! ! ! ! ! ! })
}
if i == nil { return Duplicate(c) }
SignalSource(func(done chan bool) {
i.Each(c)
done <- true
}).Wait()
return n.Interface()
}
74. package main
import “fmt”
import . “generalise”
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 := Transformation( func(x interface{}) interface{} { return x.(int) * 2 } ).Map(s)
x := <- Combination(sum).Reduce(s, 0)
fmt.Printf("s", s, x.(int))
x = <- Combination(sum).Reduce(d, 0)
fmt.Printf("d", d, x.(int))
}
produces:
s = [0 1 2 3 4 5], sum = 15
c = [0 2 4 6 8 10], sum = 30
77. package sqlite3
/ #include <sqlite3.h>
/
import "C"
import "fmt"
import "os"
type Database struct {
handle! ! ! ! *C.sqlite3
Filename! ! ! string
Flags! ! ! ! C.int
}
func (db *Database) Error() os.Error {
! return Errno(C.sqlite3_errcode(db.handle))
}
78. const(
OK! ! ! = Errno(iota)
ERROR
CANTOPEN!= Errno(14)
)
var errText = map[Errno]string {
ERROR: ! ! "SQL error or missing database",
CANTOPEN:! "Unable to open the database file",
}
type Errno int
func (e Errno) String() (err string) {
if err = errText[e]; err == "" {
err = fmt.Sprintf("errno %v", int(e))
}
return
}
79. func (db *Database) Open(flags... int) (e os.Error) {
db.Flags = 0
for _, v := range flags { db.Flags = db.Flags | C.int(v) }
f := C.CString(db.Filename)
if err := Errno(C.sqlite3_open_v2(f, &db.handle, db.Flags, nil)); err != OK {
e = err
} else if db.handle == nil {
e = CANTOPEN
}
return
}
func (db *Database) Close() {
! C.sqlite3_close(db.handle)
! db.handle = nil
}
80. func Open(filename string, flags... int) (db *Database, e os.Error) {
defer func() {
if x := recover(); x != nil {
db.Close()
db = nil
e = ERROR
}
}()
db = &Database{ Filename: filename }
if len(flags) == 0 {
e = db.Open(
C.SQLITE_OPEN_FULLMUTEX,
C.SQLITE_OPEN_READWRITE,
C.SQLITE_OPEN_CREATE)
} else {
e = db.Open(flags...)
}
return
}
81. func (db *Database) Prepare(sql string, values... interface{})
(s *Statement, e os.Error) {
s = &Statement{ db: db, timestamp: time.Nanoseconds() }
rv := Errno(C.sqlite3_prepare_v2(db.handle, C.CString(sql), -1, &s.cptr, nil))
switch {
case rv != OK:!! ! return nil, rv
case len(values) > 0:! e, _ = s.BindAll(values...)
}
return
}
func (db *Database) Execute(sql string, f... func(*Statement, ...interface{}))
(c int, e os.Error) {
var st! *Statement
switch st, e = db.Prepare(sql); e {
case nil:! ! c, e = st.All(f...)
!
case OK:! ! e = nil
}
return
}
85. 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)
}
}
}()
}
}
86. 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")
}
87. 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
89. package raw
import . "reflect"
type Slice struct { *SliceValue }
func (s *Slice) Set(i int, value interface{}) {
s.Elem(i).SetValue(NewValue(value))
}
func (s *Slice) Overwrite(offset int, source interface{}) {
switch source := source.(type) {
case *Slice:! s.Overwrite(offset, *source)
!
case Slice:!! reflect.Copy(s.SliceValue.Slice(offset, s.Len()), source.SliceValue)
default:!! ! switch v := NewValue(source).(type) {
! ! ! ! ! case *SliceValue:! ! s.Overwrite(offset, Slice{v})
! ! ! ! ! default:! ! ! ! ! s.Set(offset, v.Interface())
! ! ! ! ! }
}
}
90. package main
import . "fmt"
import . "raw"
func main() {
report := "%v (%v) = %v of %v: %vn"
m := make([]int, 2)
Printf(report, "m", "cells", len(m), cap(m), m)
b := ByteSlice(m)
Printf(report, "b", "bytes", len(b), cap(b), b)
Overwrite(m, []byte{0, 0, 0, 1, 0, 0, 0, 1})
Printf(report, "m", "cells", len(m), cap(m), m)
}
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]
93. 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)) }
}
94. 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())
}
95. package instructions
type Assembler struct {
opcodes map[string] int
names 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) Define(names... string) {
for _, name := range names {
a.opcodes[name] = len(a.names)
a.names[len(a.names)] = name
}
}
96. package instructions
func (a Assembler) Assemble(name string, params... int) (i Instruction) {
i = make(Instruction, len(params) + 1)
switch opcode, ok := a.opcodes[name]; {
case ok:! i[0] = opcode
!
default:!! i[0] = INVALID_OPCODE
}
copy(i[1:], params)
return
}
97. 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
}
98. 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])
}
}
}
99. produces:
noop
load! ! 1
store!1, 2
unknown
op = 2 result = 3
100. CISC
semantically rich instructions
complex memory addressing modes
compact binary code
101. RISC
separate IO and data processing
register-to-register instructions
load/store memory access
102. VLIW
multiple operations per instruction
compiler statically determines parallelism
simplifies control logic
104. 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"
}
105. 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
}
106. 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)
top == 0 { panic(CALL_STACK_UNDERFLOW) }
c.PC, c.CS = c.CS[top - 1], c.CS[:top]
}
107. 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++
}
108. 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) }
}
}
}
109. 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) }
}
}
110. 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)
}
}
111. 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
112. accumulator machine
1-operand instructions
data from memory combined with accumulator
result stored in accumulator
113. 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) }
}
114. 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)
}
})
}
115. 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
117. 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)!}
}
118. 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]
}
119. 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)
}
})
}
120. 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]
121. register machine
multi-operand instructions
data read from memory into registers
operator combines registers and stores
122. 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)! }
}
123. 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)
}
})
}
124. 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]
125. transport triggering
register machine architecture
exposes internal buses and components
operations are side-effects of internal writes
126. vector machine
multi-operand instructions
data vectors read from memory into registers
operations combine registers
127. 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)! ! }
}
128. package vmachine
import . "processor"
func (v *VMachine) Load(r int, m []int) {
! v.R[r] = make([]int, len(m))
! copy(v.R[r], m)
}
129. 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)
}
})
}
130. 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] [] [] [] [] [] [] []]
131. hypercube
n-dimensional multi-operand instructions
data matrices read from memory into registers
operations combine registers
133. close to the machine
transport buses
peripheral drivers
hardware acceleration