-
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 three 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,
/* ... */
};
const size_t OpcodeMaxOperands = 3;
struct Opcode {
OpcodeType type;
OpcodeHandler handler;
Operand operands[OpcodeMaxOperands];
};
| 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, a | jumps to off if a is not 0 |
| jmpz | off, a | jumps to off if a 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 |
|---|---|---|
| nop | do nothing | |
| 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: assign e, f # e = f
Or:
0: jmpz 4, a 1: jmpz 4, b 2: call c, [a] 3: store d 4: 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?