# 2017q3 Homework3 (simulator)
contributed by <`HMKRL`>
---
## 觀察 full-stack-hello
由 `driver.c` 開始觀察,得知 vm 啟動的程式碼如下:
```C
case LOAD_ELF_AND_EVAL: {
vm_env *env = vm_new();
load_from_elf(env, in_fd);
hook_opcodes(env);
vm_run(env);
vm_free(env);
break;
}
```
vm_env 的結構如下
```C
struct __vm_env {
vm_inst insts[INSTS_MAX_SIZE]; /* Program instructions */
vm_value cpool[CPOOL_MAX_SIZE]; /* Constant pool */
vm_value temps[TEMPS_MAX_SIZE]; /* Temporary storage */
vm_opcode_impl impl[OPCODE_IMPL_MAX_SIZE]; /* OPCODE impl */
vm_regs r;
int insts_count;
int cpool_count;
int temps_count;
};
```
function call stack 位於 temps
其中 register 部份
```
typedef struct {
size_t pc; // program counter.
size_t sp; // stack runs from the end of 'temps' region.
size_t from; // the immediate PC before last branch/return.
size_t to; // the immediate PC after last branch/return.
} vm_regs;
```
`vm_run` 的部份大量使用 macro, 因此採用 `gcc -E` 觀察展開後的 `vm_run`
```
const static void *labels[] = {&&OP_HALT, &&OP_ADD, &&OP_SUB, &&OP_PRINT, &&OP_JLT, &&OP_JLE, &&OP_JZ, &&OP_JGE, &&OP_JGT, &&OP_JNZ, &&OP_JMP, &&OP_MUL, &&OP_DIV, &&OP_MOD, &&OP_CALL, &&OP_RET, &&OP_AND, &&OP_OR, &&OP_NOT, &&OP_XOR, &&OP_LSL, &&OP_LSR, &&OP_ASR,}; \
goto *labels[env->insts[env->r.pc].opcode];
```
此處採用了 gcc computed goto 跳至 pc 所指向的 instruction, 以避免 switch-case 造成的 branch:
參考[此處](https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables),其中解釋 computed goto 細節:
- 不像 switch-case 需要確認資料是否在範圍內(`default` 需要),因此可以減少指令數目
- jump 至不同位置的程式碼是分開的,因此有利於處理器 branch-prediction. switch-case 的 jump 位於同一個指令,較難對各個情況的 branch 進行預測
:::warning
說話不精確,改用 computed goto 仍有 branch,請詳細描述具體落差,以及用計算機結構的觀點闡述
:notes: jserv
:::
```C
typedef struct {
int opcode;
vm_operand op1;
vm_operand op2;
int result;
} vm_inst;
```
每個 instruction 會透過以下方法執行:
```
OP_ADD : do {
if (env->impl[env->insts[env->r.pc].opcode].handler)
env->impl[env->insts[env->r.pc].opcode].handler(
vm_get_op_value(env, &env->insts[env->r.pc].op1),
vm_get_op_value(env, &env->insts[env->r.pc].op2),
vm_get_temp_value(env, env->insts[env->r.pc].result)
);
do {
++env->r.pc;
goto *labels[env->insts[env->r.pc].opcode];
} while (0);
} while (0);
```
TO [HTYISABUG](https://github.com/HTYISABUG): `do{} while(0);` 是為了避免單行 if 沒加大括弧造成巨集不完整的手法([參考此處](https://stackoverflow.com/questions/923822/whats-the-use-of-do-while0-when-we-define-a-macro))
:::danger
術語叫做 [dangling else](https://en.wikipedia.org/wiki/Dangling_else)
:notes: jserv
:::
## ISA 格式
參考 `tests/mul.s`
```
mul $3 $2 #1 ; multiply: (3)*(2)
print #1
mul $3 $-2 #2 ; multiply: (3)*(-2)
print #2
```
可知 `$X` 代表常數,`#X` 則是 register
可用的 register 上限則是 `CPOOL_MAX_SIZE`