implementing virtual machines in go & c

71
implementing virtual machines in Go & C Eleanor McHugh @feyeleanor http://github.com/feyeleanor

Upload: eleanor-mchugh

Post on 19-Jan-2017

144 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Implementing Virtual Machines in Go & C

implementing virtual machines

in Go & CEleanor McHugh

@feyeleanor

http://github.com/feyeleanor

Page 2: Implementing Virtual Machines in Go & C

I made my own machine Yes, we're building steam I hate the same routine

— Building Steam, Abney Park

Page 3: Implementing Virtual Machines in Go & C

software is where machine meets thought

Page 4: Implementing Virtual Machines in Go & C

and as programmers we think a lot about thinking

Page 5: Implementing Virtual Machines in Go & C

whilst thinking very little about machines

Page 6: Implementing Virtual Machines in Go & C

we style ourselves philosophers & wizards

Page 7: Implementing Virtual Machines in Go & C

when machines need us to be engineers & analysts

Page 8: Implementing Virtual Machines in Go & C

so this is a talk about machine philosophy

Page 9: Implementing Virtual Machines in Go & C

framed in a language we humans can understand

Page 10: Implementing Virtual Machines in Go & C

so let's learn to love our Turing machines

Page 11: Implementing Virtual Machines in Go & C

by building other Turing machines' in their image

Page 12: Implementing Virtual Machines in Go & C

system virtualisation

Page 13: Implementing Virtual Machines in Go & C

hardware emulation

Page 14: Implementing Virtual Machines in Go & C

program execution

Page 15: Implementing Virtual Machines in Go & C

timely

stateful

conversational

discrete

Page 16: Implementing Virtual Machines in Go & C

despatch loops

fetch instruction

decode

execute

Page 17: Implementing Virtual Machines in Go & C

c: switch

bytes or tokens

portable

#include <stdio.h>#include <stdlib.h>#include "stack.h"

#define READ_OPCODE *PC++

typedef enum {PUSH = 0,ADD,PRINT,EXIT

} opcodes;

int program [] = {(int)PUSH, 13,(int)PUSH, 28,(int)ADD,PRINT,EXIT,

};

STACK *S;

void interpret(int *PC) {int l, r;while (1) {

switch(READ_OPCODE) {case PUSH:

S = push(S, READ_OPCODE);break;

case ADD:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);break;

case PRINT:printf(“%d + %d = %d\n, l, r, S->data);break;

case EXIT:return;

}}

}

int main() {interpret(program);

}

Page 18: Implementing Virtual Machines in Go & C

go: switch

constants

package mainimport "fmt"

func main() {interpret([]interface{}{

PUSH, 13,PUSH, 28,ADD,PRINT,EXIT,

})}

type stack struct {data inttail *stack

}

func (s *stack) Push(v int) (r *stack) {r = &stack{data: v, tail: s}return

}

func (s *stack) Pop() (v int, r *stack) {return s.data, s.tail

}

type OPCODE int

const (PUSH = OPCODE(iota)ADDPRINTEXIT

)

func interpret(p []interface{}) {var l, r intS := 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 = %v\n", l, r, S.data)

case EXIT:return

}} else {

return}

}}

Page 19: Implementing Virtual Machines in Go & C

c: direct call

pointer to function

multi-byte

portable

#include <stdio.h>#include <stdlib.h>#include "stack.h"

#define READ_OPCODE *PC++typedef void (*opcode)();

STACK *S;opcode *PC;

void op_add_and_print() {int l, r;S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);printf("%d + %d = %d\n", l, r, S->data);

}

void op_exit() {exit(0);

}

opcode program [] = {op_push, (opcode)(long)13,op_push, (opcode)(long)28,op_add_and_print,op_exit

};

int main() {PC = program;while (1) {

(READ_OPCODE)();}

}

Page 20: Implementing Virtual Machines in Go & C

go: direct call

package mainimport "fmt"import "os"

func main() {p := new(Interpreter)p.m = []interface{}{

p.Push, 13,p.Push, 28,p.Add, p.Print, p.Exit,

}p.Run()

}

type stack struct {data inttail *stack

}

func (s *stack) Push(v int) (r *stack) {return &stack{data: v, tail: s}

}

func (s *stack) Pop() (v int, r *stack) {return s.data, s.tail

}

type Interpreter struct {S *stackl, r, PC intm []interface{}

}

func (i *Interpreter) read_program() (r interface{}) {return i.m[i.PC]

}

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 = %v\n", i.l, i.r, i.S.data)

}

func (i *Interpreter) Exit() {os.Exit(0)

}

Page 21: Implementing Virtual Machines in Go & C

go: direct call

raw closures

package main

import "fmt"import "os"

type stack struct {data inttail *stack

}

func (s *stack) Push(v int) (r *stack) {return &stack{data: v, tail: s}

}

func (s *stack) Pop() (v int, r *stack) {return s.data, s.tail

}

type Primitive func(*Interpreter)

type Interpreter struct {S *stackl, r, PC intm []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 = %v\n", i.l, i.r, i.S.data)},func (i *Interpreter) { os.Exit(0) },},

}p.Run()

}

Page 22: Implementing Virtual Machines in Go & C

go: direct call

raw closures

func main() {p := &Interpreter{

m: []Primitive{func(i *Interpreter) {

i.S = i.S.Push(13)i.PC++i.read_program()(i)i.PC++i.read_program()(i)

},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 = %v\n", i.l, i.r, i.S.data)},func (i *Interpreter) {

os.Exit(0)},

},}p.Run()

}

Page 23: Implementing Virtual Machines in Go & C

go: direct call

raw closures

type Primitive func()

func (i *Interpreter) Run() {for {

i.read_program()()i.PC++

}}

func main() {i := new(Interpreter)

npush := func(vals ...int) Primitive {return func() {

for _, v := range vals {i.S = i.S.Push(v)

}}

}

i.m = []Primitive{npush(13, 28),func() {

i.l, i.S = i.S.Pop()i.r, i.S = i.S.Pop()i.S = i.S.Push(i.l + i.r)

},func() {

fmt.Printf("%v + %v = %v\n", i.l, i.r, i.S.data)},func() { os.Exit(0) },

}i.Run()

}

Page 24: Implementing Virtual Machines in Go & C

go: direct call

raw closures

jit assembly

package main

import "fmt"import "strings"import "os"

type stack struct {data inttail *stack

}

func (s *stack) Push(v int) (r *stack) {return &stack{data: v, tail: s}

}

func (s *stack) Pop() (v int, r *stack) {return s.data, s.tail

}

func (s *stack) String() string {r := []string{}for i := s; i != nil; i = i.tail {

r = append(r, fmt.Sprint(i.data))}return "[" + strings.Join(r, ", ") + "]"

}

Page 25: Implementing Virtual Machines in Go & C

go: direct call

raw closures

jit assembly

type Label stringtype labels map[Label] int

type VM struct {S *stackl, r, PC intm []interface{}labels

}

func (v *VM) read_program() interface{} {return v.m[v.PC]

}

func (v *VM) String() string {return fmt.Sprintf("@pc[%v] => #{%v}, %v", v.PC, v.m[v.PC], v.S)

}

func (v *VM) Load(program ...interface{}) {v.labels = make(labels)v.PC = -1for _, 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 = -1for {

v.PC++v.read_program().(func())()

}}

Page 26: Implementing Virtual Machines in Go & C

go: direct call

raw closures

jit assembly

type Interpreter struct { VM }

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) JumpIfNotZero() {i.PC++if i.S.data != 0 {

i.PC = i.m[i.PC].(int)}

}

func main() {i := new(Interpreter)

print_state := func() {fmt.Println(i)

}skip := func() { i.PC++ }i.Load(

i.Push, 13,Label("decrement"),func() {

i.S = i.S.Push(-1)},i.Add,print_state,skip,print_state,i.JumpIfNotZero, Label("decrement"),print_state,func() {

os.Exit(0)},

)i.Run()

}

Page 27: Implementing Virtual Machines in Go & C

c: indirect thread

local jumps

gcc/clang specific

indirect loading

#include <stdio.h>#include <stdlib.h>#include "stack.h"

typedef enum {PUSH = 0, ADD, PRINT, EXIT

} opcodes;

void interpret(int *program) {static void *opcodes [] = {

&&op_push,&&op_add,&&op_print,&&op_exit

};

int l, r;STACK *S;int *PC = program;goto *opcodes[*PC++];

op_push:S = push(S, *PC++);goto *opcodes[*PC++];

op_add:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);goto *opcodes[*PC++];

op_print:printf("%d + %d = %d\n", l, r, S->data);goto *opcodes[*PC++];

op_exit:return;

}

int main() {int program [] = {

PUSH, 13,PUSH, 28,ADD,PRINT,EXIT

};interpret(program);

}

Page 28: Implementing Virtual Machines in Go & C

c: direct thread

jit local jumps

gcc/clang specific

direct loading

void interpret(int *PC, int words) {static void *dispatch_table[] = {

&&op_push,&&op_add,&&op_print,&&op_exit

};

STACK *S;int l, r;void **program = compile(PC, words, dispatch_table);if (program == NULL)

exit(1);goto **program++;

op_push:S = push(S, (int)(long)*program++);goto **program++;

op_add:S = pop(S, &l);S = pop(S, &r);S = push(S, l + r);goto **program++;

op_print:printf("%d + %d = %d\n", l, r, S->data);goto **program++;

op_exit:return;

}

int main() {int program[] = {

PUSH, 13,PUSH, 28,ADD,PRINT,EXIT

};interpret(program, 7);

}

Page 29: Implementing Virtual Machines in Go & C

c: direct thread

#define INTERPRETER(body, ...) \DISPATCHER(__VA_ARGS__); \void **p = compile(PC, d); \if (p == NULL) \

exit(1); \EXECUTE_OPCODE \body

#define DISPATCHER(...) \static void *d[] = { __VA_ARGS__ }

#define READ_OPCODE \*p++

#define EXECUTE_OPCODE \goto *READ_OPCODE;

#define PRIMITIVE(name, body) \name: body; \EXECUTE_OPCODE

void interpret(int *PC) {STACK *S;int l, r;INTERPRETER(

PRIMITIVE(push,S = push(S, (int)(long)READ_OPCODE)

)PRIMITIVE(add,

S = pop(S, &l);S = pop(S, &r);S = push(S, l + r)

)PRIMITIVE(print,

printf("%d + %d = %d\n", l, r, S->data))PRIMITIVE(exit,

return),&&push, &&add, &&print, &&exit)

}

Page 30: Implementing Virtual Machines in Go & C

c: direct thread

jit local jumps

gcc/clang specific

direct loading

void **compile(int *PC, int words, void *dispatch_table[]) {static void *compiler [] = {

&&comp_push,&&comp_add,&&comp_print,&&comp_exit

};

if (words < 1)return NULL;

void **program = malloc(sizeof(void *) * words);void **cp = program;goto *compiler[*PC++];

comp_push:*cp++ = dispatch_table[PUSH];*cp++ = (void *)(long)*PC++;words -= 2;if (words == 0)

return program;goto *compiler[*PC++];

comp_add:*cp++ = dispatch_table[ADD];words--;if (words == 0)

return program;goto *compiler[*PC++];

comp_print:*cp++ = dispatch_table[PRINT];words--;if (words == 0)

return program;goto *compiler[*PC++];

comp_exit:*cp++ = dispatch_table[EXIT];words--;if (words == 0)

return program;goto *compiler[*PC++];

}

Page 31: Implementing Virtual Machines in Go & C

registers

operands

local caching

Page 32: Implementing Virtual Machines in Go & C

vm harness

dispatch

program

program counter

package main

import "fmt"

type Label stringtype labels map[Label] int

type VM struct {PC intm []interface{}labels

}

func (v *VM) read_program() interface{} {return v.m[v.PC]

}

func (v *VM) String() string {return fmt.Sprintf("@pc[%v] => #{%v}, %v", v.PC, v.m[v.PC])

}

func (v *VM) Load(program ...interface{}) *VM {v.labels = make(labels)v.PC = -1for _, token := range program {

v.assemble(token)}return v

}

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 = -1for {

v.PC++v.read_program().(func())()

}}

Page 33: Implementing Virtual Machines in Go & C

stack machine

zero operands

package main

import "fmt"import "os"

type S []inttype StackMachine struct {

SVM

}

func (s *StackMachine) String() string {return fmt.Sprintf("%v, %v", s.VM, s.S)

}

func (s *StackMachine) Push() {s.PC++s.S = append(s.S, s.read_program().(int))

}

func (s *StackMachine) Add() {s.S[len(s.S) - 2] += s.S[len(s.S) - 1]s.S = s.S[:len(s.S) - 1]

}

func (s *StackMachine) JumpIfNotZero() {s.PC++if s.S[len(s.S) - 1] != 0 {

s.PC = s.m[s.PC].(int)}

}

func main() {s := new(StackMachine)print_state := func() {

fmt.Println(s)}skip := func() { s.PC++ }s.Load(

s.Push, 13,Label("dec"),func() { s.S = append(s.S, -1) },s.Add,print_state,skip,print_state,s.JumpIfNotZero, Label("dec"),print_state,func() { os.Exit(0) },

).Run()}

Page 34: Implementing Virtual Machines in Go & C

accumulator machine

single register

single operand

package main

import "fmt"import "os"

type AccMachine struct {A intVM

}

func (a *AccMachine) String() string {return fmt.Sprintf("%v, A=%v", a.VM, a.A)

}

func (a *AccMachine) Clear() {a.A = 0

}

func (a *AccMachine) LoadValue() {a.PC++a.A = a.read_program().(int)

}

func (a *AccMachine) Add() {a.PC++a.A += a.read_program().(int)

}

func (a *AccMachine) JumpIfNotZero() {a.PC++if a.A != 0 {

a.PC = a.m[a.PC].(int)}

}

func main() {a := new(AccMachine)print_state := func() {

fmt.Println(a)}skip := func() { a.PC++ }a.Load(

a.Clear,a.LoadValue, 13,Label("decrement"),a.Add, -1,print_state,skip,print_state,a.JumpIfNotZero, Label("decrement"),print_state,func() { os.Exit(0) },

).Run()}

Page 35: Implementing Virtual Machines in Go & C

register machine

multi-register

multi-operand

package main

import "fmt"import "os"

type RegMachine struct {R []intVM

}

func (r *RegMachine) String() string {return fmt.Sprintf("%v, R=%v", r.VM, r.R)

}

func (r *RegMachine) Clear() {r.R = make([]int, 2, 2)

}

func (r *RegMachine) read_value() int {r.PC++return r.read_program().(int)

}

func (r *RegMachine) LoadValue() {r.R[r.read_value()] = r.read_value()

}

func (r *RegMachine) Add() {i := r.read_value()j := r.read_value()r.R[i] += r.R[j]

}

func (r *RegMachine) JumpIfNotZero() {if r.R[r.read_value()] != 0 {

r.PC = r.read_value()} else {

r.PC++}

}

func main() {r := new(RegMachine)print_state := func() {

fmt.Println(r)}skip := func() { r.PC++ }r.Load(

r.Clear,r.LoadValue, 0, 13,r.LoadValue, 1, -1,Label("decrement"),r.Add, 0, 1,print_state,skip,print_state,r.JumpIfNotZero, 0, Label("decrement"),print_state,func() { os.Exit(0) },

).Run()}

Page 36: Implementing Virtual Machines in Go & C

vector machine

matrix machine

hypercube

graph processor

any datatype can be a register

Page 37: Implementing Virtual Machines in Go & C

memory model

instructions

computation

state

Page 38: Implementing Virtual Machines in Go & C

memory model

opcodes

stack

heap

Page 39: Implementing Virtual Machines in Go & C

heaps

word-aligned

contiguous

byte-addressable

Page 40: Implementing Virtual Machines in Go & C

go: slice heap

package mainimport r "reflect"import "unsafe"

type Memory []uintptr

var _BYTE_SLICE = r.TypeOf([]byte(nil))var _MEMORY = r.TypeOf(Memory{})var _MEMORY_BYTES = int(_MEMORY.Elem().Size())

func (m Memory) newHeader() (h r.SliceHeader) {h = *(*r.SliceHeader)(unsafe.Pointer(&m))h.Len = len(m) * _MEMORY_BYTESh.Cap = cap(m) * _MEMORY_BYTESreturn

}

func (m *Memory) Bytes() (b []byte) {h := m.newHeader()return *(*[]byte)(unsafe.Pointer(&h))

}

func (m *Memory) Serialise() (b []byte) {h := m.newHeader()b = make([]byte, h.Len)copy(b, *(*[]byte)(unsafe.Pointer(&h)))return

}

func (m *Memory) Overwrite(i interface{}) {switch i := i.(type) {case Memory:

copy(*m, i)case []byte:

h := m.newHeader()b := *(*[]byte)(unsafe.Pointer(&h))copy(b, i)

}}

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

m.Overwrite(Memory{3, 5})fmt.Println("m (cells) =", len(m), "of", cap(m), ":", m)fmt.Println("b (bytes) =", len(b), "of", cap(b), ":", b)fmt.Println("s (bytes) =", len(s), "of", cap(s), ":", s)

s = m.Serialise()m.Overwrite([]byte{8, 7, 6, 5, 4, 3, 2, 1})fmt.Println("m (cells) =", len(m), "of", cap(m), ":", m)fmt.Println("b (bytes) =", len(b), "of", cap(b), ":", b)fmt.Println("s (bytes) =", len(s), "of", cap(s), ":", s)

}

Page 41: Implementing Virtual Machines in Go & C

ruby: c heap

require "fiddle"

class Fiddle::PointerNIL = Pointer.new(0)SIZE = Fixnum::SIZE

PACKING_PATTERN = case SIZEwhen 2 then "S"when 4 then "L"when 8 then "Q"end + "!"

def write(value)str = Fiddle::format(value)pad = Fiddle::padding(str)l = pad + str.lengthraise BufferOverflow.new(self, l) if l > sizeself[0, l] = str + 0.chr * padself + l

end

def to_bin[self].pack(PACKING_PATTERN)

endend

Page 42: Implementing Virtual Machines in Go & C

Home (./index.html) Classes(./index.html#classes) Methods(./index.html#methods)

In Files

fiddle/closure.c

fiddle/fiddle.c

fiddle/lib/fiddle.rb

fiddle/lib/fiddle/closure.rb

fiddle/lib/fiddle/cparser.rb

fiddle/lib/fiddle/function.rb

fiddle/lib/fiddle/import.rb

fiddle/lib/fiddle/pack.rb

fiddle/lib/fiddle/struct.rb

fiddle/lib/fiddle/types.rb

fiddle/lib/fiddle/value.rb

Namespace

MODULE Fiddle::BasicTypes (Fiddle/BasicTypes.html)

MODULE Fiddle::CParser (Fiddle/CParser.html)

MODULE Fiddle::CStructBuilder (Fiddle/CStructBuilder.html)

MODULE Fiddle::Importer (Fiddle/Importer.html)

MODULE Fiddle::Win32Types (Fiddle/Win32Types.html)

CLASS Fiddle::CStruct (Fiddle/CStruct.html)

CLASS Fiddle::CStructEntity (Fiddle/CStructEntity.html)

CLASS Fiddle::CUnion (Fiddle/CUnion.html)

CLASS Fiddle::CUnionEntity (Fiddle/CUnionEntity.html)

CLASS Fiddle::Closure (Fiddle/Closure.html)

CLASS Fiddle::CompositeHandler (Fiddle/CompositeHandler.html)

CLASS Fiddle::DLError (Fiddle/DLError.html)

CLASS Fiddle::Function (Fiddle/Function.html)

CLASS Fiddle::Handle (Fiddle/Handle.html)

CLASS Fiddle::Pointer (Fiddle/Pointer.html)

Methods

::dlopen (#method-c-dlopen)

::dlunwrap (#method-c-dlunwrap)

::dlwrap (#method-c-dlwrap)

::free (#method-c-free)

::last_error (#method-c-last_error)

::last_error= (#method-c-last_error-3D)

::malloc (#method-c-malloc)

::realloc (#method-c-realloc)

::win32_last_error (#method-c-win32_last_error)

::win32_last_error= (#method-c-win32_last_error-3D)

Fiddle (./Fiddle.html)

Fiddle::BasicTypes (./Fiddle/BasicTypes.html)

Fiddle::CParser (./Fiddle/CParser.html)

Fiddle::CStruct (./Fiddle/CStruct.html)

Fiddle::CStructBuilder (./Fiddle/CStructBuilder.html)

Fiddle::CStructEntity (./Fiddle/CStructEntity.html)

Fiddle::CUnion (./Fiddle/CUnion.html)

Fiddle::CUnionEntity (./Fiddle/CUnionEntity.html)

Fiddle::Closure (./Fiddle/Closure.html)

Fiddle::Closure::BlockCaller (./Fiddle/Closure/BlockCaller.html)

Fiddle::CompositeHandler (./Fiddle/CompositeHandler.html)

Fiddle::DLError (./Fiddle/DLError.html)

Fiddle::Function (./Fiddle/Function.html)

Fiddle::Handle (./Fiddle/Handle.html)

Fiddle::Importer (./Fiddle/Importer.html)

Fiddle::Pointer (./Fiddle/Pointer.html)

Fiddle::Win32Types (./Fiddle/Win32Types.html)

(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSIP27JCWA4YKQWCAADEKQICKBI4Z3JCESIE53UC6BDK27KC6BD553UCWSIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSIP27JCWA4YKQWCAADEKQICKBI4Z3JCESIE53UC6BDK27KC6BD553UCWSIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

ads via Carbon(http://carbonads.net/)

Home (http://yoururlhere.com/) Core (http://yoururlhere.com/core) Std-lib (http://yoururlhere.com/stdlib) Downloads (http://yoururlhere.com/downloads)

Search

FiddleA libffi wrapper for Ruby.

Description¶ (#module-Fiddle-label-Description) ↑ (#top)

Fiddle (Fiddle.html) is an extension to translate a foreign function interface (FFI) withruby.

It wraps libffi (http://sourceware.org/libffi/), a popular C library which provides a portableinterface that allows code written in one language to call code written in anotherlanguage.

Example¶ (#module-Fiddle-label-Example) ↑ (#top)

Here we will use Fiddle::Function (Fiddle/Function.html) to wrap floor(3) from libm(http://linux.die.net/man/3/floor)

require 'fiddle'

libm = Fiddle.dlopen('/lib/libm.so.6')

floor = Fiddle::Function.new(

libm['floor'],

[Fiddle::TYPE_DOUBLE],

Fiddle::TYPE_DOUBLE

)

puts floor.call(3.14159) #=> 3.0

frozen_string_literal: false

frozen_string_literal: false

frozen_string_literal: false

frozen_string_literal: false

Constants

ALIGN_CHARALIGN_CHARALIGN_CHAR (Fiddle.html#ALIGN_CHAR)

The alignment size of a char

ALIGN_DOUBLEALIGN_DOUBLEALIGN_DOUBLE (Fiddle.html#ALIGN_DOUBLE)

The alignment size of a double

ALIGN_FLOATALIGN_FLOATALIGN_FLOAT (Fiddle.html#ALIGN_FLOAT)

The alignment size of a float

ALIGN_INTALIGN_INTALIGN_INT (Fiddle.html#ALIGN_INT)

The alignment size of an int

ALIGN_INTPTR_TALIGN_INTPTR_TALIGN_INTPTR_T (Fiddle.html#ALIGN_INTPTR_T)

The alignment size of a intptr_t

ALIGN_LONGALIGN_LONGALIGN_LONG (Fiddle.html#ALIGN_LONG)

The alignment size of a long

Class/Module Index

access DLLs

call C functions

Page 43: Implementing Virtual Machines in Go & C

Home (../index.html) Classes(../index.html#classes) Methods(../index.html#methods)

In Files

fiddle/closure.c

Parent

Object

Methods

::[] (#method-c-5B-5D)

::malloc (#method-c-malloc)

::new (#method-c-new)

::to_ptr (#method-c-to_ptr)

#+ (#method-i-2B)

#+@ (#method-i-2B-40)

#- (#method-i-2D)

#-@ (#method-i-2D-40)

#<=> (#method-i-3C-3D-3E)

#== (#method-i-3D-3D)

#[] (#method-i-5B-5D)

#[]= (#method-i-5B-5D-3D)

#eql? (#method-i-eql-3F)

#free (#method-i-free)

#free= (#method-i-free-3D)

#inspect (#method-i-inspect)

#null? (#method-i-null-3F)

#ptr (#method-i-ptr)

#ref (#method-i-ref)

#size (#method-i-size)

#size= (#method-i-size-3D)

#to_i (#method-i-to_i)

#to_int (#method-i-to_int)

#to_s (#method-i-to_s)

#to_str (#method-i-to_str)

#to_value (#method-i-to_value)

Fiddle (../Fiddle.html)

Fiddle::BasicTypes (../Fiddle/BasicTypes.html)

Fiddle::CParser (../Fiddle/CParser.html)

Fiddle::CStruct (../Fiddle/CStruct.html)

Fiddle::CStructBuilder (../Fiddle/CStructBuilder.html)

Fiddle::CStructEntity (../Fiddle/CStructEntity.html)

Fiddle::CUnion (../Fiddle/CUnion.html)

Fiddle::CUnionEntity (../Fiddle/CUnionEntity.html)

Fiddle::Closure (../Fiddle/Closure.html)

Fiddle::Closure::BlockCaller (../Fiddle/Closure/BlockCaller.html)

Fiddle::CompositeHandler (../Fiddle/CompositeHandler.html)

Fiddle::DLError (../Fiddle/DLError.html)

Fiddle::Function (../Fiddle/Function.html)

Fiddle::Handle (../Fiddle/Handle.html)

Fiddle::Importer (../Fiddle/Importer.html)

Fiddle::Pointer (../Fiddle/Pointer.html)

Fiddle::Win32Types (../Fiddle/Win32Types.html)

(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

ads via Carbon(http://carbonads.net/)

(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

Top software engineersare using IndeedPrime to land theirdream job.(http://yoururlhere.com//srv.carbonads.net/ads/click/x/GTND423UFTSI52JECWB4YKQWCAADEK3JCAADCZ3JCESIE53MFT7IK2QKC6BD553UCABIKK3EHJNCLSIZ?

segment=placement:rubydocorg;)

ads via Carbon(http://carbonads.net/)

Home (http://yoururlhere.com/) Core (http://yoururlhere.com/core) Std-lib (http://yoururlhere.com/stdlib) Downloads (http://yoururlhere.com/downloads)

Search

Fiddle::PointerFiddle::Pointer (Pointer.html) is a class to handle C pointers

Public Class Methods

Get the underlying pointer for ruby object val and return it as a Fiddle::Pointer (Pointer.html) object.

Allocate size bytes of memory and associate it with an optional freefunc that will be called when thepointer is garbage collected.

freefunc must be an address pointing to a function or an instance of Fiddle::Function (Function.html)

Create a new pointer to address with an optional size and freefunc.

freefunc will be called when the instance is garbage collected.

Get the underlying pointer for ruby object val and return it as a Fiddle::Pointer (Pointer.html) object.

Public Instance Methods

Returns a new pointer instance that has been advanced n bytes.

Returns a new Fiddle::Pointer (Pointer.html) instance that is a dereferenced pointer for this pointer.

Analogous to the star operator in C.

Returns a new pointer instance that has been moved back n bytes.

Returns a new Fiddle::Pointer (Pointer.html) instance that is a reference pointer for this pointer.

Analogous to the ampersand operator in C.

Returns -1 if less than, 0 if equal to, 1 if greater than other.

Returns nil if ptr cannot be compared to other.

Returns true if other wraps the same pointer, otherwise returns false.

Returns integer stored at index.

Class/Module Index

Fiddle::Pointer[val] => cptrto_ptr(val) => cptr

Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance

Fiddle::Pointer.new(address) => fiddle_cptrnew(address, size) => fiddle_cptrnew(address, size, freefunc) => fiddle_cptr

to_ptr(val) => cptr

ptr + n => new cptr

ptr

ptr - n => new cptr

ref

ptr <=> other => -1, 0, 1, or nil

ptr == other => true or false

ptr[index] → an_integerptr[start, length] → a_string

MRI stdlib

C pointers

malloc

not portable

Page 44: Implementing Virtual Machines in Go & C

ruby: c heap

def Fiddle::Pointer.format(value)value.respond_to?(:to_bin) ? value.to_bin : Marshal.dump(value)

end

Page 45: Implementing Virtual Machines in Go & C

ruby: c heap

require "fiddle"

class Fiddle::PointerNIL = Pointer.new(0)SIZE = Fixnum::SIZE

PACKING_PATTERN = case SIZEwhen 2 then "S"when 4 then "L"when 8 then "Q"end + "!"

def write(value)str = Fiddle::format(value)pad = Fiddle::padding(str)l = pad + str.lengthraise BufferOverflow.new(self, l) if l > sizeself[0, l] = str + 0.chr * padself + l

end

def to_bin[self].pack(PACKING_PATTERN)

endend

Page 46: Implementing Virtual Machines in Go & C

ruby: c heap

class FixnumSIZE = 1.size

PACKING_PATTERN = case SIZEwhen 2 then "s"when 4 then "l"when 8 then "q"end + "!"

def to_bin[self].pack(PACKING_PATTERN)

end

def self.read_bin(pointer)pointer[0, SIZE].unpack(PACKING_PATTERN).first

endend

Page 47: Implementing Virtual Machines in Go & C

ruby: c heap

m = Fiddle::Pointer.malloc 64begin m.write(0.chr * 59)

m.write(0.chr * 60)m.write(0.chr * 61)

rescue Fiddle::BufferOverflow => ep e.message

end

"Buffer overflow: 72 bytes at #<Fiddle::Pointer:0x007f8849052160 ptr=0x007f8849051da0 size=64 free=0x00000000000000>"

Page 48: Implementing Virtual Machines in Go & C

ruby: c heap

s = "Hello, Terrible Memory Bank!"i = 4193f = 17.00091

m.write(i)puts m.readq = m.write(-i)puts m.read

q.write(s)puts q.read(String)

r = q.write(s[0, s.length - 1])puts q.read(String)

t = r.write(f)puts r.read(Float)

t.write(-f)puts t.read(Float)

=> 4193

=> -4193

=> Hello, Terrible Memory Bank!

=> Hello, Terrible Memory Bank

=> 17.00091

=> -17.00091

Page 49: Implementing Virtual Machines in Go & C

stacks

sequential

bounded depth

push & pop

Page 50: Implementing Virtual Machines in Go & C

sequential

push data on

pop data off

Page 51: Implementing Virtual Machines in Go & C

contiguous

bounded depth

Page 52: Implementing Virtual Machines in Go & C

c: array stack

fixed size

malloc to create

realloc to resize

#include <stdlib.h>#define STACK_MAX 100

typedef enum {STACK_OK = 0,STACK_OVERFLOW,STACK_UNDERFLOW

} STACK_STATUS;

typedef struct stack STACK;struct stack {

int data[STACK_MAX];int size;

};

STACK *NewStack() {STACK *s;s = malloc(sizeof(STACK));s->size = 0;return s;

}

int depth(STACK *s) {return s->size;

}

STACK_STATUS push(STACK *s, int data) {if (s->size < STACK_MAX) {

s->data[s->size++] = data;return STACK_OK;

}return STACK_OVERFLOW;

}

STACK_STATUS pop(STACK *s, int *r) {if (s->size > 0) {

*r = s->data[s->size - 1];s->size--;return STACK_OK;

}return STACK_UNDERFLOW;

}

Page 53: Implementing Virtual Machines in Go & C

go: slice stack

package main

import "fmt"

type stack []int

func (s *stack) Push(data int) {(*s) = append((*s), data)

}

func (s *stack) Pop() (r int) {sp := len(*s) - 1r = (*s)[sp]*s = (*s)[:sp]return

}

func (s stack) Depth() int {return len(s)

}

func main() {s := new(stack)s.Push(1)s.Push(3)fmt.Printf("depth = %d\n", s.Depth())l := s.Pop()r := s.Pop()fmt.Printf("%d + %d = %d\n", l, r, l+r)fmt.Printf("depth = %d\n", s.Depth())

}

Page 54: Implementing Virtual Machines in Go & C

singly linked list

functional

shared elements

immutability

Page 55: Implementing Virtual Machines in Go & C

c: cactus stack

nil is empty

grows on push

manual GC

#include <stdlib.h>

typedef struct stack STACK;struct stack {

int data;STACK *next;

};

STACK *push(STACK *s, int data) {STACK *r = malloc(sizeof(STACK));r->data = data;r->next = s;return r;

}

STACK *pop(STACK *s, int *r) {if (s == NULL)

exit(1);*r = s->data;return s->next;

}

int depth(STACK *s) {int r = 0;for (STACK *t = s; t != NULL; t = t->next) {

r++;}return r;

}

void gc(STACK **old, int items) {STACK *t;for (; items > 0 && *old != NULL; items--) {

t = *old;*old = (*old)->next;free(t);

}}

Page 56: Implementing Virtual Machines in Go & C

c: cactus stack

nil is empty

grows on push

manual GC

#include <stdio.h>#include <cactus_stack.h>

int sum(STACK *tos) {int a = 0;for (int p = 0; tos != NULL;) {

tos = pop(tos, &p);a += p;

}return a;

}

void print_sum(STACK *s) {printf("%d items: sum = %d\n", depth(s), sum(s));

}

int main() {STACK *s1 = push(NULL, 7);STACK *s2 = push(push(s1, 7), 11);s1 = push(push(push(s1, 2), 9), 4);

STACK *s3 = push(s1, 17);s1 = push(s1, 3);print_sum(s1);print_sum(s2);print_sum(s3);

}

Page 57: Implementing Virtual Machines in Go & C

go: cactus stack

nil is empty

grows on push

automatic GC

package main

import "fmt"

type stack struct {data inttail *stack

}

func (s stack) Push(v int) (r stack) {r = stack{data: v, tail: &s}return

}

func (s stack) Pop() (v int, r stack) {return s.data, *s.tail

}

func (s stack) Depth() (r int) {for t := s.tail; t != nil; t = t.tail {

r++}return

}

func (s *stack) PrintSum() {fmt.Printf("sum(%v): %v\n", s.Depth(), s.Sum())

}

func (s stack) Sum() (r int) {for t, n := s, 0; t.tail != nil; r += n {

n, t = t.Pop()}return

}

func main() {s1 := new(stack).Push(7)s2 := s1.Push(7).Push(11)s1 = s1.Push(2).Push(9).Push(4)s3 := s1.Push(17)s1 = s1.Push(3)

s1.PrintSum()s2.PrintSum()s3.PrintSum()

}

Page 58: Implementing Virtual Machines in Go & C

caches

word-aligned

discontiguous

label-addressable

Page 59: Implementing Virtual Machines in Go & C

c: hash map

array

associative arrays

search

#include <limits.h>

struct map {int size;assoc_array_t **chains;

};typedef struct map map_t;

map_t *map_new(int size) {map_t *m = malloc(sizeof(map_t));m->chains = malloc(sizeof(assoc_array_t*) * size);for (int i = 0; i < size; i++) {

m->chains[i] = NULL;}m->size = size;return m;

}

int map_chain(map_t *m, char *k) {unsigned long int b;for (int i = strlen(k) - 1; b < ULONG_MAX && i > 0; i--) {

b = b << 8;b += k[i];

}return b % m->size;

}

char *map_get(map_t *m, char *k) {search_t *s = search_find(m->chains[map_chain(m, k)], k);if (s != NULL) {

return s->value;}return NULL;

}

void map_set(map_t *m, char *k, char *v) {int b = map_chain(m, k);assoc_array_t *a = m->chains[b];search_t *s = search_find(a, k);if (s->value != NULL) {

s->cursor->value = strdup(v);} else {

assoc_array_t *n = assoc_array_new(k, v);if (s->cursor == a) {

n->next = s->cursor;m->chains[b] = n;

} else if (s->cursor == NULL) {s->memo->next = n;

} else {n->next = s->cursor;s->memo->next = n;

}}free(s);

}

Page 60: Implementing Virtual Machines in Go & C

c: hash map

array

associative arrays

search

#include <stdlib.h>#include <string.h>

struct assoc_array {char *key;void *value;struct assoc_array *next;

};typedef struct assoc_array assoc_array_t;

assoc_array_t *assoc_array_new(char *k, char *v) {assoc_array_t *a = malloc(sizeof(assoc_array_t));a->key = strdup(k);a->value = strdup(v);a->next = NULL;return a;

}

char *assoc_array_get_if(assoc_array_t *a, char *k) {char *r = NULL;if (a != NULL && strcmp(a->key, k) == 0) {

r = strdup(a->value);}return r;

}

Page 61: Implementing Virtual Machines in Go & C

c: hash map

array

associative arrays

search

struct search {char *term, *value;assoc_array_t *cursor, *memo;

};typedef struct search search_t;

search_t *search_new(assoc_array_t *a, char *k) {search_t *s = malloc(sizeof(search_t));s->term = k;s->value = NULL;s->cursor = a;s->memo = NULL;return s;

}

void search_step(search_t *s) {s->value = assoc_array_get_if(s->cursor, s->term);

}

int searching(search_t *s) {return s->value == NULL && s->cursor != NULL;

}

search_t *search_find(assoc_array_t *a, char *k) {search_t *s = search_new(a, k);for (search_step(s); searching(s); search_step(s)) {

s->memo = s->cursor;s->cursor = s->cursor->next;

}return s;

}

Page 62: Implementing Virtual Machines in Go & C

c: hash map

array

associative arrays

search

#include <stdio.h>#include “map.h

int main( int argc, char **argv ) {map_t *m = map_new(1024);map_set(m, "apple", "rosy");printf("%s\n", map_get(m, "apple"));map_set(m, "blueberry", "sweet");printf("%s\n", map_get(m, "blueberry"));map_set(m, "cherry", "pie");printf("%s\n", map_get(m, "cherry"));map_set(m, "cherry", "tart");printf("%s\n", map_get(m, "cherry"));printf("%s\n", map_get(m, “tart"));

}

rosy

sweet

pie

tart(null)

Page 63: Implementing Virtual Machines in Go & C

go: assoc array

searcher

package main

type AssocArray struct {Key stringValue interface{}Next *AssocArray

};

func (a *AssocArray) GetIf(k string) (r interface{}) {if a != nil && a.Key == k {

r = a.Value}return

}

type Search struct {Term stringValue 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

}

func Find(a *AssocArray, k string) (s *Search) {s = &Search{ Term: k, Cursor: a }for s.Step(); s.Searching(); s.Step() {

s.Memo = s.Cursors.Cursor = s.Cursor.Next

}return

}

Page 64: Implementing Virtual Machines in Go & C

go: assoc array

searcher

package main

type AssocArray struct {Key stringValue interface{}Next *AssocArray

};

func (a *AssocArray) GetIf(k string) (r interface{}) {if a != nil && a.Key == k {

r = a.Value}return

}

type Search struct {Term stringValue 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

}

func Find(a *AssocArray, k string) (s *Search) {s = &Search{ Term: k, Cursor: a }for s.Step(); s.Searching(); s.Step() {

s.Memo = s.Cursors.Cursor = s.Cursor.Next

}return

}

Page 65: Implementing Virtual Machines in Go & C

ruby: hash map

associative array

searcher

slice of arrays

package main

type Map []*AssocArray

func (m Map) Chain(k string) int {var c uintfor i := len(k) - 1; i > 0; i-- {

c = c << 8c += (uint)(k[i])

}return int(c) % len(m)

}

func (m Map) Get(k string) (r interface{}) {if s := Find(m[m.Chain(k)], k); s != nil {

r = s.Value}return

}

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.Cursorm[c] = n

case s.Cursor == nil:s.Memo.Next = n

default:n.Next = s.Cursors.Memo.Next = n

}}

}

Page 66: Implementing Virtual Machines in Go & C

ruby: map

hashmap v. native

package mainimport "fmt"

func main() {hashmap():nativemap();

}

func hashmap() {m := make(Map, 1024)m.Set("apple", "rosy")fmt.Printf("%v\n", m.Get("apple"))

m.Set("blueberry", "sweet")fmt.Printf("%v\n", m.Get("blueberry"))

m.Set("cherry", "pie")fmt.Printf("%v\n", m.Get("cherry"))

m.Set("cherry", "tart")fmt.Printf("%v\n", m.Get("cherry"))

fmt.Printf("%v\n", m.Get("tart"))}

func nativemap() {m := make(map[string] interface{})m["apple"] = "rosy"fmt.Printf("%v\n", m["apple"])

m["blueberry"] = "sweet"fmt.Printf("%v\n", m["blueberry"])

m["cherry"] = "pie"fmt.Printf("%v\n", m["cherry"])

m["cherry"] = "tart"fmt.Printf("%v\n", m["cherry"])

fmt.Printf("%v\n", m["tart"])}

Page 67: Implementing Virtual Machines in Go & C

Go & cgo: integratingexisting C code with GoAndreas Krennmair <[email protected]>

Golang User Group Berlin

Twitter: @der_ak

cgo

inline c

Page 68: Implementing Virtual Machines in Go & C

Feb 5, 2013 at 10:28PM

Caleb DoxseyCaleb Doxsey / / BlogBlog / / Go & AssemblyGo & Assembly

Go & AssemblyGo & AssemblyCaleb Doxsey

One of my favorite parts about Go is its unwavering focus on utility. Sometimes we placeso much emphasis on language design that we forget all the other things programminginvolves. 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 getcode.google.com/p/go.net/websocket )

Like all languages Go has a set of style guidelines, some enforced by the compiler(like Uppercase vs lowercase) and others merely conventional, but it also has a toolto clean up code: gofmt name_of_file.go .

And there's also go fix which can automatically convert Go code designed forearlier versions to newer versions

Go comes with a tool to test packages: go test /path/to/package . It can dobenchmarks too.

Go is debuggable and can be profiled.

Did you know there's a playground to try out Go online?

Go can interact with C libraries via cgo.

Those are just a few examples, but I want to focus on one that's not generally well known:Go can seamlessly use functions written in AssemblyGo can seamlessly use functions written in Assembly.

assembly

Page 69: Implementing Virtual Machines in Go & C

SIMD

branch prediction

cache misses

memory latency

Page 70: Implementing Virtual Machines in Go & C

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 download Create new file Upload files Find file master Branch: New pull request

Latest commit 9ee7fe0 5 days ago feyeleanor implementations of popular VM architectural styles

README.md

vm-fragmentsA 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:

http://slideshare.net/feyeleanor

Videos for these talks are available at:

https://www.infoq.com/presentations/virtual-machine-go-chttps://www.youtube.com/watch?v=b3ji5tdZUfY

Contact GitHub API Training Shop Blog About© 2016 GitHub, Inc. Terms Privacy Security Status Help

1 01 Unwatch Star Fork

source code

Page 71: Implementing Virtual Machines in Go & C