# 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`