Virtual Machines
Representations
Goal: Translate Expression
function {
local_vars = [],
constants = [1, 2, None],
instructions = [
load_const 0
load_const 1
add
return
]
}
f = fun() {
return 1 + 2;
}
Example
Translate:
1 + 2
Example
Translate:
1 + 2
Translation: Variable Reference
// Description: load value of local variable and push onto operand stack
// Mnemonic: load_local i
// Operand 0: index of local variable to read
// Stack: S => S :: value_of(f.local_vars[i])
LoadLocal,
// Description: load value of global variable
// Mnemonic: load_global i
// Operand 0: index of name of global variable in enclosing function's names list // Stack: S => S :: global_value_of(f.names[i])
LoadGlobal
Virtual Machine
x = 1;
f = fun(y) {
return x + y;
};
f(1);
function {
functions = [ ],
constants = [None, 0, 1],
names = [x, f],
instructions = [
load_const 2
store_global 0
load_const 1
load_func 0
alloc_closure
store_global 1
load_global 1
load_const 2
call 1
pop
load_const 0
return
]
}
function {
local_vars = [y],
constants = [None],
names = [x]
instructions = [
load_global 0
load_local 0
add
return
]
}
Organization/Representation: Goals
Data Organization (Interpreter)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z.f;
};
g({f : y + 1});
};
f(x + 1);
Frame | |
x | 1 |
f | |
p | |
Frame | |
y | 2 |
g | |
p | |
Record { f : 1 }
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
p | |
Closure{ p : }
Closure{ p : }
Organization: Naming/Addressing
Variable | Interpreter | VM |
Global | Name (Hash) | Name (Hash) |
Local | Name (Hash) | Index (Array) |
Field | Name (Hash) | Name (Hash) |
Free | Pointer Chasing | Reference |
Symbol Tables
Hierarchy In Symbol Tables
Definitions
Symbol Table
Statically (in the compiler) compute for each scope
x = 1;
f = fun(y) {
g = fun (z) {
return x + y + z;
};
return g;
};
f(2)(3);
Symbol Table | |
x | global |
f | global |
p | |
Symbol Table
Statically (in the compiler) compute for each scope
x = 1;
f = fun(y) {
g = fun (z) {
return x + y + z;
};
return g;
};
f(2)(3);
Symbol Table | |
x | global |
f | global |
p | |
Symbol Table | |
y | local |
g | local |
p | |
Symbol Table
Statically (in the compiler) compute for each scope
x = 1;
f = fun(y) {
g = fun (z) {
return x + y + z;
};
return g;
};
f(2)(3);
Symbol Table | |
x | global |
f | global |
p | |
Symbol Table | |
y | local, ref |
g | local |
p | |
Symbol Table | |
z | local |
p | |
Symbol Table
Statically (in the compiler) compute for each scope
x = 1;
f = fun(y) {
g = fun (z) {
return x + y + z;
};
return g;
};
f(2)(3);
Symbol Table | |
x | global |
f | global |
p | |
Symbol Table | |
y | local |
g | local |
p | |
Code Generation
Example:
function {
functions = [ ],
constants = [None, 0, 1],
names = [x, f],
instructions = [
load_const 2
store_global 0
load_const 1
load_func 0
alloc_closure
store_global 1
load_global 1
load_const 2
call 1
pop
load_const 0
return
]
}
function {
local_vars = [y],
constants = [None],
names = [x]
instructions = [
load_global 0
load_local 0
add
return
]
}
x = 1;
f = fun(y) {
return x + y;
};
f(1);
Efficient Representations (bytecode and other low-level) representations leverage static information about the program to enable and enforce efficient encodings of the program’s execution
Organization: Naming/Addressing
Variable | Interpreter | VM |
Global | Name (Hash) | Name (Hash) |
Local | Name (Hash) | Index (Array) |
Field | Name (Hash) | Name (Hash) |
Free | Pointer Chasing | Reference |
Locals as Maps
Frame |
|
|
|
|
|
void Frame::store(vector<Bucket*> frame, string name, Value* val)
{
uint64_t h = hash(name);
Bucket* bucket = frame[h % frame.size()];
Bucket* b = new Bucket(bucket, name, val);
frame[h % frame.size()] = b;
}
fun()
{
x = 1;
y = 2;
z = 3;
};
Bucket | |
x | 1 |
| |
Bucket | |
y | 2 |
| |
Bucket | |
z | 3 |
| |
Value* Frame::lookup(vector<Bucket*> frame, string name)
{
uint64_t h = hash(name);
Bucket* bucket = frame[h % frame.size()];
return bucket->find(name);
}
Locals as Arrays
fun()
{
x = 1;
y = 2;
z = 3;
};
function {
local_vars = [x, y, z],
constants = [1, 2, 3]
instructions = [
load_const 0
store_local 0
load_const 1
store_local 1
load_const 2
store_local 2
]}
void Frame::store(vector<Value *> frame, uint64_t index, Value *val)
{
frame[index] = val;
}
Locals |
1 |
2 |
3 |
Value* Frame::lookup(vector<Value *> frame, uint64_t index)
{
return frame[index];
}
Free Variables
Closure{ p : }
Closure{ p : }
Heap
Frame | |
x | |
f | |
p | |
Frame | |
y | |
g | |
p | |
Stack |
|
|
|
Globals |
|
Frame | |
z | |
p | |
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
1 |
3 |
2 |
Free Variables (Faster)
Closure{ p : }
Closure{ p : }
Heap
Frame | |
x | |
f | |
p | |
Frame | |
y | |
g | |
p | |
Stack |
|
|
|
Globals |
|
Frame | |
z | |
p | |
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
1 |
3 |
2 |
Reference{ }
Free Variables (Faster)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
Frame | |
x | |
f | |
p | |
Frame | |
y | |
g | |
p | |
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
p | |
Closure{ [ ]}
2 |
Reference{ }
3 |
1 |
Closure{ p : }
Free Variables (Faster)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
Frame | |
x | |
f | |
p | |
Frame | |
y | |
g | |
p | |
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
y | |
Closure{ [ ]}
2 |
Reference{ }
3 |
1 |
Closure{ p : }
Free Variables (Faster)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
Frame | |
x | |
f | |
p | |
Frame | |
y | |
g | |
p | |
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
y | |
Closure{ [ ]}
2 |
Reference{ }
3 |
1 |
Closure{ p : }
Free Variables (Faster)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
Frame | |
x | |
f | |
Frame | |
y | |
g | |
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
y | |
Closure{ [ ]}
Closure{ [ ] }
2 |
Reference{ }
3 |
1 |
Free Variables (Faster)
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
Frame | |
x | |
f | |
Frame | |
y | |
g | |
Stack |
|
|
|
Heap
Globals |
|
Frame | |
z | |
y | |
Closure{ [ ]}
Closure{ [ ] }
2 |
Reference{ }
3 |
1 |
function {
local_vars = [z],
free_vars = [y]
names = [x]
instructions = [
load_global 0
push_ref 0
load_ref
add
load_local 0
add
return
]}
Code Gen
function {
local_vars = [z],
free_vars = [y]
names = [x]
instructions = [
load_global 0
push_ref 0
load_ref
add
load_local 0
add
return
]}
x = 1;
f = fun(y) {
g = fun(z) {
return x + y + z;
};
return g;
};
f(2)(3);
function {
functions = [ ],
local_vars = [y, g],
local_ref_vars [y],
free_vars = []
names = []
instructions = [
load_func 0
push_ref 0
alloc_closure 1
store_local 1
load_local 1
return
]}
g (from previous slide)
f
Conclusion
Efficient Representations (bytecode and other low-level) representations leverage static information about the program to enable and enforce efficient encodings of the program’s execution
Records as Maps
fun() {
r = {
x : 1;
};
}
function
{
local_vars = [r],
constants = [1],
names = [x]
instructions = [
alloc_record
dup
load_const 0
field_store 0
store_local 0
]}
Locals |
|
Record |
|
|
|
|
|
Bucket | |
x | 1 |
| |
Significant Impact of Dynamic Behavior
fun() {
r = {
x : 1;
};
}
f = fun(y) {
r = {
x : 1;
};
r[y] = 2;
};
f(input());
Locals |
|
Record |
|
|
|
|
|
Bucket | |
x | 1 |
| |
Bucket | |
Hi! | 2 |
| |
Records: Maps (Revisited)
fun() {
r = {
x : 1;
};
}
function
{
local_vars = [r],
constants = [1],
names = [x]
instructions = [
alloc_record
dup
load_const 0
field_store 0
store_local 0
]}
Locals |
|
Record |
|
|
|
|
|
Bucket | |
x | 1 |
| |
Records: Maps (Revisited)
fun() {
r = {
x : 1;
};
}
function
{
local_vars = [r],
constants = [1],
names = [x]
instructions = [
alloc_record
dup
load_const 0
field_store 0
store_local 0
]}
Locals |
|
Record |
|
|
|
|
|
Bucket | |
x | 1 |
| |
Value* Frame::lookup(vector<Bucket*> frame, string name)
{
uint64_t h = hash(name);
Bucket* bucket = frame[h % frame.size()];
return bucket->find(name);
}
void Frame::store(vector<Bucket*> frame, string name, Value* val)
{
uint64_t h = hash(name);
Bucket* bucket = frame[h % frame.size()];
Bucket* b = new Bucket(bucket, name, val);
frame[h % frame.size()] = b;
}
Records: Maps (Revisited)
fun() {
r = {
x : 1;
};
}
function
{
local_vars = [r],
constants = [1],
names = [x]
instructions = [
alloc_record
dup
load_const 0
field_store 0
store_local 0
]}
Locals |
|
Record |
|
|
|
|
|
Bucket | |
x | 1 |
| |
Value* Frame::lookup(vector<Bucket*> frame, uint64_t h,
string name)
{
Bucket* bucket = frame[h % frame.size()];
return bucket->find(name);
}
void Frame::store(vector<Bucket*> frame, string name, uint64_t h,
Value* val)
{
uint64_t h = hash(name);
Bucket* bucket = frame[h % frame.size()];
Bucket* b = new Bucket(bucket, name, val);
frame[h % frame.size()] = b;
}