--- tags: sysprog2017 --- # 2017q3 Homework3 (simulator) <contributed by `HexRabbit`> ### 作業沒寫完對不起各位納稅人 ## 作業說明 https://hackmd.io/hFGdxT1JRPa8SbdofdNgQw ## 心得筆記 - 讀取記憶體相比數值運算來的慢 - cacheline競爭會大幅降低效能 - <s>多線程</s> 多執行緒可能會有shared cacheline不一致問題 -> cacheline中的資料被修改後,對其他cpu來說會成為無效資料 - 硬體本身可能也會對操作優化 - vm.c 中的do{...}while(0)是做什麼用的? [(使用define的函式時可以加上`;`)](https://stackoverflow.com/questions/9495962/why-use-do-while-0-in-macro-definition) - __vm_env 與 vm_env 有什麼關係? 找半天原來vm_env是個typedef定義在vm.h (相當不直覺,應該在同一文件定義) - `#define OP(name) OP_##name` `##`串接前後字串 ## 程式架構 driver.c: as_exec的主邏輯設計 opcode.c: 定義各個opcode(除call,ret,helt,jmp類)的實做 vm.c: opcode(call,ret,helt,jmp類)的實做,虛擬機器初始化,暫存器定義 as.c: 語法解析,另存成可運行的結構(存於vm_env內) elf.c: 產生elf檔案用 -- vm_value: 儲存`char *`或`int`的結構 vm_operand: 儲存運算子 vm_inst: 儲存運算符 vm_seg_info: 資料串列 vm_regs: 儲存pc,sp,from(目前pc在ret後的上一個inst),to(目前branch的第一個inst) vm_env: 紀錄目前虛擬機狀態 vm_opcode_impl: 儲存opcode實做 vm_handler: function pointer,可用來模擬OOP中的function ## 錯誤修正 ```C static void and_impl(VM_HANDLER_ARGS) { - if (VM_T(op1) == INT && VM_T(op1) == INT) { + if (VM_T(op1) == INT && VM_T(op2) == INT) { VM_INT(result) = VM_INT(op1) & VM_INT(op2); } else { UNIMPL; } } static void or_impl(VM_HANDLER_ARGS) { - if (VM_T(op1) == INT && VM_T(op1) == INT) { + if (VM_T(op1) == INT && VM_T(op2) == INT) { VM_INT(result) = VM_INT(op1) | VM_INT(op2); } else { UNIMPL; } } ``` [issue](https://github.com/sysprog21/full-stack-hello/pull/3) if 判斷式中重複檢查了兩次 VM_T(op1) == INT,應將 op1 改為 op2。 ```python f.write('''/* File opcode.h was generated by scripts/gen_opcode.py. */ #ifndef OPCODE_H #define OPCODE_H + #include "vm.h" /* opcode listing */ enum {\n''') ``` [issue](https://github.com/sysprog21/full-stack-hello/pull/4) 由 python 腳本生成的 opcode.h 使用到外部定義的 type vm_env 而未引入 vm.h 可能導致編譯錯誤,將其引入即可。 :::danger 請提交 pull request,謝謝 :notes: jserv ::: :::info 搞定! :notes: HexRabbit ::: ## 功能修改 ### 於 call 和 jmp 類實做 $ 及 # 功能 觀察程式碼時發現 JMP 類與 CALL 在呼叫時不會檢查參數的型別是 CONST 還是 TEMP,預設使用者將填入`#X`(TEMP 型別),並直接取出參數(`value.id`)作為 pc 的目標,不僅使得以`$X`呼叫時會出錯外(`value.id` != 在 cpool 中的 index),使用`#X`呼叫卻完全不理會 stack 中存的值,僅僅只是作為一個符號來使用也相當的不符合程式寫作的邏輯。 重新修改為支援`#`從stack取值,以`$`使用常數操作 ```C +#define VM_CALL() \ + do { \ + vm_push(env, env->r.pc); \ + size_t n = vm_get_op_value(env, &OPCODE.op1)->value.vint; \ + GOTO(n); \ + } while (0) + +#define VM_JMP() \ + do { \ + size_t n = vm_get_op_value(env, &OPCODE.op1)->value.vint; \ + GOTO(n); \ } while (0) ``` ### 實做label 修改 make_from_operand() ```C + case ':':; + int label = vm_find_label(env, &data[1]); + if (~label) { + op.type = CONST; + op.value.id = vm_add_const(env, INT, &label); + break; + } else { + printf("Error: specified label name '%s' not found.\n", data); + free(line); + exit(-1); + break; + } ``` 修改 assemble_from_fd() ```C while (getline(&line, &size, fp) != -1) { if (line[0] == ';' || line[0] == '\n') continue; /* Remove trailing newline feed */ line[strcspn(line, "\r\n")] = 0; + if (line[strlen(line) - 1] == ':') { + line[strlen(line) - 1] = 0; + vm_make_label(env, line, insts_count); + } else + insts_count++; + } + + fseek(fp, 0, SEEK_SET); + + while (getline(&line, &size, fp) != -1) { + if (line[0] == ';' || line[0] == '\n' || line[strlen(line) - 2] == ':') + continue; + /* Remove trailing newline feed */ + line[strcspn(line, "\r\n")] = 0; assemble_line(env, line); } free(line); ``` 採用 djb2 作為 hash function,並暫時選擇 49157 作為 hash table size。 ## Fibonacci 實做 ### iterative ```=0 add $5 $0 #0 ; init to 5 jz #0 $8 add $0 $0 #1 add $1 $0 #2 add #1 #2 #2 sub #2 #1 #1 sub #0 $1 #0 jnz #0 $4 print #1 ``` 與一般撰寫程式差不多,很直覺。 ### recursive ```=0 add $5 $0 #2 ; init to 5 jmp $14 jz #2 $13 ; if 0 sub #2 $1 #3 jz #3 $12 ; if 1 sub #2 $1 #2 call $2 add #2 $1 #2 sub #2 $2 #2 call $2 add #2 $2 #2 ret add #0 $1 #0 ; add 1 to val ret call $2 print #0 ``` jmp 到後面然後再 call 回來的作法實在不是很直觀,就算我以前有寫一點 shellcode 的經驗還是花了些思考時間在上面。 使用此方式若數字過大的話會 overflow 到 heap 上。 ## 參考資料 1. https://www.lingholic.com/how-many-words-do-i-need-to-know-the-955-rule-in-language-learning-part-2/ 2. http://onlinelibrary.wiley.com/doi/10.1111/j.0039-3193.2004.00109.x/pdf