-
Notifications
You must be signed in to change notification settings - Fork 16
RFC #0001 Compiler and Virtual Machine
heuripedes edited this page Jan 6, 2012
·
9 revisions
The opcodes are executed and their results (if any) are stored in a stack. Temporary jumps (call, for example) will have the return address stored on the stack.
The opcode must carry information about itself (type), it's handler and it's operands. Opcodes are allowed to have at most two operands, of which the use will be documented soon, and will also carry some data - either a Value pointer or a signed integer. There must also be a field to indicate the operand type.
Simplified implementation proposal:
typedef void (*OpcodeHandler)(Opcode&, size_t*);
enum OperandType {
OPERAND_NONE = 0x00
OPERAND_VALUE = 0x01,
OPERAND_NUMBER = 0x02,
OPERAND_STACK = 0x03
};
struct Operand {
OperandType type;
union {
Value* value;
ssize_t number;
} data;
};
enum OpcodeType {
OP_NOP = 0x00,
/* ... */
};
struct Opcode {
OpcodeType type;
OpcodeHandler handler;
Operand operands[2];
};
| Opcode | Operands | Description |
|---|---|---|
| add | a b | a + b |
| sub | a b | a - b |
| mul | a b | a * b |
| div | a b | a / b |
| mod | a b | a % b |
| shr | a b | a << b |
| shl | a b | a >> b |
| Opcode | Operands | Description |
|---|---|---|
| bwor | a b | a | b |
| bwand | a b | a & b |
| bwxor | a b | a ^ b |
| bwnot | a | ~a |
Most of these are not used for conditionals but for inline code like
a = b == c.
| Opcode | Operands | Description |
|---|---|---|
| or | a b | a || b |
| and | a b | a && b |
| not | a | !a |
| eq | a b | a == b |
| neq | a b | a != b |
| lt | a b | a < b |
| gt | a b | a > b |
| le | a b | a <= b |
| ge | a b | a >= b |
| Opcode | Operands | Description |
|---|---|---|
| varset | a b | a = b |
| load | a | pushes a's value into the stack |
| store | a | pops the stack into a
|
| Opcode | Operands | Description |
|---|---|---|
| jmp | off | jumps to off
|
| jmpnz | off | pops the stack and jumps to off if the pop'd value is
not 0 |
| jmpz | off | pops the stack and jumps to off if the pop'd value is
0 |
| fcall | a args | pushes the current address and calls a and passes
args
|
| mcall | a args | same as fcall, but for methods. |
| ret | a | pushes a into the stack and returns to the caller |
| break | ? | ? |
| continue | ? | ? |
| Opcode | Operands | Description |
|---|---|---|
| discard | n | discards the top n values from the stack |
Listing #1:
if (a || b) {
d = c(a);
}
e = f;
Compiles to:
0: or a, b # a || b 1: jmpz 4 # leave the if 2: call c, [a] # c(a) 3: store d # d = (return from c(a)) 4: varset e, f # e = f
Or:
0: load a 1: jmpz 2: load b 3: jmpz 4: call c, [a] 5: store d 6: assign e, f
Listing #2:
Int a = 30;
Compiles to:
0: assign a 30 # see questions
Listing #4:
a = f() + 10;
Compiles to:
0: call f, [] # return value on the stack 1: add (stack), a # stack.pop() + a 2: store a # a = stack.pop()
Listing #5:
f(); /* returns something */
Compiles to:
0: call f, [] # calls f 1: discard 1 # discards return value
- How should the compiler/virtual machine handle variable declaration?
- What about constructors?