forked from rzubek/CSLisp
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbytecode design.txt
More file actions
89 lines (64 loc) · 3.78 KB
/
bytecode design.txt
File metadata and controls
89 lines (64 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
VM STATE
fn closure that contains the current code block
pc program counter; index to the code array
env linked list of execution environments (each map variables to values)
stack stack of heterogeneous values (numbers, strings, environments, closures, etc)
argcount transient argument count, used when calling functions
OPCODES
Internally, opcodes are not raw bytes - they're structs that hold a type, and arguments
in a form that's already processed (e.g. lists are lists, and not serialized byte arrays).
LABEL n label for jumps, only gets used during compilation
PUSH_CONST x push constant x onto the stack
LOCAL_GET i, j read a local variable's value and push onto stack (ith frame, jth variable in that frame)
LOCAL_SET i, j copy top-of-stack into a local variable (ith frame, jth variable in that frame)
GLOBAL_GET name push a global variable's value onto stack
GLOBAL_SET name copy top-of-stack into a global variable
DUPLICATE duplicate the topmost entry on the stack
STACK_POP pop top-of-stack, discarding the value
JMP_IF_TRUE label pop stack, and if the top value is true, go to label
JMP_IF_FALSE label pop stack, and if the top value is not true, go to label
JMP_TO_LABEL label go to label, don't touch the stack
SAVE_RETURN label make a return point continuation and push it on the stack.
the continuation is a combo of current code block and instruction index for the specified label.
MAKE_ENV n make a new env frame that points to current env as parent, then move n values from stack into local vars
(top of stack becomes the last var) and set this new env as current env.
MAKE_ENVDOT n same as MAKE_ENV but supports varargs. makes an env with n + 1 local vars,
first n arguments from call get stored in n local vars, and rest in cons list at var n + 1
MAKE_CLOSURE fn create a new closure from fn argument and current environment, and push it on top of the stack.
fn is expected to be the actual code block that will be the body of the closure.
JMP_CLOSURE n pop closure from top of the stack and set execution pointer there; n is number of args on stack to pass.
return point is not saved here; if desired, compiler should emit a call to SAVE_RETURN first.
RETURN_VAL jump to the saved return point while preserving the return value.
(by convention, return value is on top of the stack, while continuation is second.
this will pop the return value, pop the continuation, push return value back down, and then jump.)
CALL_PRIMOP name performs a primitive function call right off the stack, and stores return val back on stack.
it is assumed that the primitive function will pop all its arguments, but *not* push the return value.
INTEROP CONVENTIONS
We adopt the Scheme notion of true/false values being different from nil,
which is used mainly as a list terminator. This maps cleanly to C# semantics.
C# FORM CSLISP FORM
null nil
true #t
false #f
IMPLEMENTATION STEPS DONE
+ Cons, Symbol, Package
+ S-Exp Parser
+ Trivial compile (atoms / quotes / begin only)
+ Environments, SET!, global variable lookup
+ Symbol packages
+ IF statements, jumps
+ Function compilation, local variable lookup
+ Tail-recursive functions
+ Function definitions, LAMBDA
+ Function calls
+ Primitive functions
+ Two-pass assembler, removing label names
+ Macros
+ Trivial assemble / optimize / execute loop
+ Optimize primitive lookup with dictionary
+ Example REPL
IMPLEMENTATION TODO
- Easier FFI / custom primitives
- .NET interop via reflection (with safety checks?!)
- Peephole optimizer
- Tracing / debugging tools, ideally