Try   HackMD

2017q3 Homework3 (simulator)

<contributed by HexRabbit>

作業沒寫完對不起各位納稅人

作業說明

https://hackmd.io/hFGdxT1JRPa8SbdofdNgQw

心得筆記

  • 讀取記憶體相比數值運算來的慢
  • cacheline競爭會大幅降低效能
  • 多線程 多執行緒可能會有shared cacheline不一致問題 -> cacheline中的資料被修改後,對其他cpu來說會成為無效資料
  • 硬體本身可能也會對操作優化
  • vm.c 中的do{}while(0)是做什麼用的? (使用define的函式時可以加上;)
  • __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

錯誤修正

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
if 判斷式中重複檢查了兩次 VM_T(op1) == INT,應將 op1 改為 op2。

       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
由 python 腳本生成的 opcode.h 使用到外部定義的 type vm_env 而未引入 vm.h 可能導致編譯錯誤,將其引入即可。

請提交 pull request,謝謝

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
jserv

搞定!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
HexRabbit

功能修改

於 call 和 jmp 類實做 $ 及 # 功能

觀察程式碼時發現 JMP 類與 CALL 在呼叫時不會檢查參數的型別是 CONST 還是 TEMP,預設使用者將填入#X(TEMP 型別),並直接取出參數(value.id)作為 pc 的目標,不僅使得以$X呼叫時會出錯外(value.id != 在 cpool 中的 index),使用#X呼叫卻完全不理會 stack 中存的值,僅僅只是作為一個符號來使用也相當的不符合程式寫作的邏輯。
重新修改為支援#從stack取值,以$使用常數操作

 +#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()

 +  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()

    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

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

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