An updated version of my talk on virtual machine cores comparing techniques in C and Go for implementing dispatch loops, stacks & hash maps.
Lots of tested and debugged code is provided as well as references to some useful/interesting books.
34. go: hash map
associative array
searcher
slice of arrays
package main
type AssocArray struct {
Key string
Value interface{}
Next *AssocArray
};
func Find(a *AssocArray, k string) (s *Search) {
s = &Search{ Term: k, Cursor: a }
for s.Step(); s.Searching(); s.Step() {
s.Memo = s.Cursor
s.Cursor = s.Cursor.Next
}
return
}
func (a *AssocArray) GetIf(k string) (r interface{}) {
if a != nil && a.Key == k {
r = a.Value
}
return
}
type Search struct {
Term string
Value interface{}
Cursor, Memo *AssocArray
};
func (s *Search) Step() *Search {
s.Value = s.Cursor.GetIf(s.Term)
return s
}
func (s *Search) Searching() bool {
return s.Value == nil && s.Cursor != nil
}
35. go: hash map
associative array
searcher
slice of arrays
package main
type AssocArray struct {
Key string
Value interface{}
Next *AssocArray
};
func Find(a *AssocArray, k string) (s *Search) {
s = &Search{ Term: k, Cursor: a }
for s.Step(); s.Searching(); s.Step() {
s.Memo = s.Cursor
s.Cursor = s.Cursor.Next
}
return
}
func (a *AssocArray) GetIf(k string) (r interface{}) {
if a != nil && a.Key == k {
r = a.Value
}
return
}
type Search struct {
Term string
Value interface{}
Cursor, Memo *AssocArray
};
func (s *Search) Step() *Search {
s.Value = s.Cursor.GetIf(s.Term)
return s
}
func (s *Search) Searching() bool {
return s.Value == nil && s.Cursor != nil
}
36. go: hash map
associative array
searcher
slice of arrays
type Map []*AssocArray
func (m Map) Chain(k string) int {
var c uint
for i := len(k) - 1; i > 0; i-- {
c = c << 8
c += (uint)(k[i])
}
return int(c) % len(m)
}
func (m Map) Set(k string, v interface{}) {
c := m.Chain(k)
a := m[c]
s := Find(a, k)
if s.Value != nil {
s.Cursor.Value = v
} else {
n := &AssocArray{ Key: k, Value: v }
switch {
case s.Cursor == a:
n.Next = s.Cursor
m[c] = n
case s.Cursor == nil:
s.Memo.Next = n
default:
n.Next = s.Cursor
s.Memo.Next = n
}
}
}
func (m Map) Get(k string) (r interface{}) {
if s := Find(m[m.Chain(k)], k); s != nil {
r = s.Value
}
return
}
37. go: hash map
hashmap
vs
native map
package main
import "fmt"
func main() {
v := make(map[string] interface{})
v["apple"] = "rosy"
v["blueberry"] = "sweet"
v["cherry"] = "pie"
fmt.Printf("%vn", v["apple"])
fmt.Printf("%vn", v["blueberry"])
fmt.Printf("%vn", v["cherry"])
v["cherry"] = "tart"
fmt.Printf("%vn", v["cherry"])
fmt.Printf("%vn", v["tart"])
m := make(Map, 1024)
m.Set("apple", "rosy")
m.Set("blueberry", "sweet")
m.Set("cherry", "pie")
fmt.Printf("%vn", m.Get("apple"))
fmt.Printf("%vn", m.Get("blueberry"))
fmt.Printf("%vn", m.Get("cherry"))
m.Set("cherry", "tart")
fmt.Printf("%vn", m.Get("cherry"))
fmt.Printf("%vn", m.Get("tart"))
}
rosy
sweet
pie
tart
(null)
rosy
sweet
pie
tart
(null)
49. go: switch
constants
package main
import "fmt"
type stack struct {
int
*stack
}
func (s *stack) Push(v int) *stack {
return &stack{v, s}
}
func (s *stack) Pop() (int, *stack) {
return s.int, s.stack
}
type OPCODE int
const (
PUSH = OPCODE(iota)
ADD
PRINT
EXIT
)
func main() {
interpret([]interface{}{
PUSH, 13,
PUSH, 28,
ADD,
PRINT,
EXIT,
})
}
func interpret(p []interface{}) {
var l, r int
S := new(stack)
for PC := 0; ; PC++ {
if op, ok := p[PC].(OPCODE); ok {
switch op {
case PUSH:
PC++
S = S.Push(p[PC].(int))
case ADD:
l, S = S.Pop()
r, S = S.Pop()
S = S.Push(l + r)
case PRINT:
fmt.Printf("%v + %v = %vn", l, r, S.int)
case EXIT:
return
}
} else {
return
}
}
}
50. go: direct call
function pointers
package main
import "fmt"
import "os"
type stack struct {
int
*stack
}
func (s *stack) Push(v int) *stack {
return &stack{v, s}
}
func (s *stack) Pop() (int, *stack) {
return s.int, s.stack
}
func main() {
p := new(Interpreter)
p.Load(
p.Push, 13,
p.Push, 28,
p.Add,
p.Print,
p.Exit,
)
p.Run()
}
type Interpreter struct {
S *stack
l, r, PC int
m []interface{}
}
func (i *Interpreter) read_program() (r interface{}) {
return i.m[i.PC]
}
func (i *Interpreter) Load(p ...interface{}) {
i.m = p
}
func (i *Interpreter) Run() {
for {
i.read_program().(func())()
i.PC++
}
}
func (i *Interpreter) Push() {
i.PC++
i.S = i.S.Push(i.read_program().(int))
}
func (i *Interpreter) Add() {
i.l, i.S = i.S.Pop()
i.r, i.S = i.S.Pop()
i.S = i.S.Push(i.l + i.r)
}
func (i *Interpreter) Print() {
fmt.Printf("%v + %v = %vn", i.l, i.r, i.S.int)
}
func (i *Interpreter) Exit() {
os.Exit(0)
}
51. go: direct call
function literals
package main
import "fmt"
import "os"
type stack struct {
int
*stack
}
func (s *stack) Push(v int) *stack {
return &stack{v, s}
}
func (s *stack) Pop() (int, *stack) {
return s.int, s.stack
}
type Primitive func(*Interpreter)
type Interpreter struct {
S *stack
l, r, PC int
m []Primitive
}
func (i *Interpreter) read_program() Primitive {
return i.m[i.PC]
}
func (i *Interpreter) Run() {
for {
i.read_program()(i)
i.PC++
}
}
func main() {
p := &Interpreter{
m: []Primitive{
func(i *Interpreter) {
i.S = i.S.Push(13)
},
func(i *Interpreter) {
i.S = i.S.Push(28)
},
func(i *Interpreter) {
i.l, i.S = i.S.Pop()
i.r, i.S = i.S.Pop()
i.S = i.S.Push(i.l + i.r)
},
func(i *Interpreter) {
fmt.Printf("%v + %v = %vn", i.l, i.r, i.S.int)
},
func (i *Interpreter) {
os.Exit(0)
},
},
}
p.Run()
}
52. go: direct call
closures
package main
import "fmt"
import "os"
type stack struct {
int
*stack
}
func (s *stack) Push(v int) *stack {
return &stack{v, s}
}
func (s *stack) Pop() (int, *stack) {
return s.int, s.stack
}
type Primitive func(*Interpreter)
type Interpreter struct {
S *stack
l, r, PC int
m []Primitive
}
func (i *Interpreter) read_program() Primitive {
return i.m[i.PC]
}
func (i *Interpreter) Run() {
for {
i.read_program()
i.PC++
}
}
func main() {
var p *Interpreter
p := &Interpreter{
m: []Primitive{
func() {
p.S = p.S.Push(13)
},
func() {
p.S = p.S.Push(28)
},
func() {
p.l, p.S = p.S.Pop()
p.r, p.S = p.S.Pop()
p.S = p.S.Push(p.l + p.r)
},
func() {
fmt.Printf("%v + %v = %vn", p.l, p.r, p.S.int)
},
func () {
os.Exit(0)
},
},
}
p.Run()
}
53. go: direct call
closures
compilation
package main
import "fmt"
import "os"
type stack struct {
int
*stack
}
func (s *stack) Push(v int) *stack {
return &stack{v, s}
}
func (s *stack) Pop() (int, *stack) {
return s.int, s.stack
}
type Primitive func()
type Label string
type labels []Label
type VM struct {
PC int
m []interface{}
labels
}
func (v *VM) Load(program ...interface{}) {
v.labels = make(labels)
v.PC = -1
for _, token := range program {
v.assemble(token)
}
}
func (v *VM) assemble(token interface{}) {
switch t := token.(type) {
case Label:
if i, ok := v.labels[t]; ok {
v.m = append(v.m, i)
} else {
v.labels[t] = v.PC
}
default:
v.m = append(v.m, token)
v.PC++
}
}
func (v *VM) Run() {
v.PC = -1
for {
v.PC++
v.read_program().(Primitive)()
}
}
func (v *VM) read_program() interface{} {
return v.m[v.PC]
}
type Interpreter struct {
VM
S *stack
l, r int
}
61. Go & cgo: integrating
existing C code with Go
Andreas Krennmair <ak@synflood.at>
Golang User Group Berlin
Twitter: @der_ak
cgo
inline c
62. Feb 5, 2013 at 10:28PM
Caleb DoxseyCaleb Doxsey // BlogBlog // Go & AssemblyGo & Assembly
Go & AssemblyGo & Assembly
Caleb Doxsey
One of my favorite parts about Go is its unwavering focus on utility. Sometimes we place
so much emphasis on language design that we forget all the other things programming
involves. For example:
Go's compiler is fast
Go comes with a robust standard library
Go works on a multitude of platforms
Go comes with a complete set of documentation available from the command line /
a local web server / the internet
All Go code is statically compiled so deployment is trivial
The entirety of the Go source code is available for perusal in an easy format online
(like this)
Go has a well defined (and documented) grammar for parsing. (unlike C++ or Ruby)
Go comes with a package management tool. go get X (for example go get
code.google.com/p/go.net/websocket )
assembly
64. This repository Pull requests Issues Gist
No description or website provided. — Edit
00 memory experiments with Fiddle::Pointer exploring use of C-style memory mana… 5 days ago
01 stacks stacks in Go 5 days ago
02 hashes roll-your-own hash maps in Go 5 days ago
03 dispatcher Go versions of dispatch loops 5 days ago
04 architecture implementations of popular VM architectural styles 5 days ago
LICENSE Initial commit 5 days ago
README.md added links for slides and video of talks based on this code 5 days ago
Search
feyeleanor / vm-fragments
Code Issues 0 Pull requests 0 Projects 0 Wiki Pulse Graphs Settings
13 commits 1 branch 0 releases 1 contributor MIT
Clone or downloadClone or downloadCreate new file Upload files Find filemasterBranch: New pull request
Latest commit 9ee7fe0 5 days agofeyeleanor implementations of popular VM architectural styles
README.md
vm-fragments
A collection of code fragments for talks I've given around implementing simple virtual machine internals in C, Go and Ruby.
The slides for these talks are available at:
1 01Unwatch Star Fork
source code