Try   HackMD

2017q3 Homework3 (simulator)

tags: sysprog2017

contributed by <amikai>

Github

程式碼研讀

一個 compilation system 通常會提供 compiler driver 幫你方便使用 preprocessor, compiler, assembler, linker, 我們在 linux 下所用的 gcc 指令就是 compiler driver. 此作業裡的 driver 也像是一個 compiler driver 的角色, 當使用 as_exec 這個執行檔寫入 elf 檔案或是執行組合語言都需要透過 driver 將虛擬機器開啟 (vm.c) 依據你的需求輸入不同的選項進行寫入 elf 或是執行.

加入 nop 指令

一開始只想說為了要瞭解一下這支程式, 那就加一個指令試試看好了, 但是要怎麼加呢? 翻翻找找看到一個 PR, 這個 PR 是加入 bitwise 指令, 所以我就嘗試加入了一個簡單的指令 nop (commit), 先在 opcode 定義 nop 指令, nop 指令是第 23 個指令, 不需要 operand 也不需要 result, 所以這樣填入即可:

nop,       23,       0,       0,          0

在 vm.c 裡定義 nop 指令, 因爲不用做事所以直接跳下一條指令就對了:

#define VM_NOP()  \
    do {          \
        DISPATCH; \
    } while (0)

然後在 vm_run 加入 nop 指令

void vm_run(vm_env *env)
{
    BEGIN_OPCODES;

    OP(ADD) : VM_CALL_HANDLER();
    OP(SUB) : VM_CALL_HANDLER();
    ...
    OP(JMP) : GOTO(OPCODE.op1.value.id);
    OP(CALL) : VM_CALL(OPCODE.op1.value.id);
    OP(RET) : VM_RET();
+   OP(NOP) : VM_NOP();

    OP(HALT) : goto terminate;

    END_OPCODES;
terminate:
    return;
}

opcode.h 就不用擔心了 codegen 會自動產生, 所以其實加入指令是非常簡單地

加入 label 功能 看到大神同學還沒做 趕緊來做

原本在 as.c 檔案裡加入很多程式碼為了要支持 label 功能, 寫完的時候發現編譯一直過不了, 最後發現 vm.h 裡的 forward declaration (typedef struct __vm_env vm_env;) 眼淚都快掉出來了, 寫到最後才發現 vm_env 這個 struct 已經被封裝起來了

要加入 label 這個功能主要有兩個功能區塊:

  1. 給定某一行程式碼一個名稱的 label
  2. 被當作 operand 的 label (ex: gnz #1 label)

我先從第 1 個功能區塊探討起, 要判斷是不是一個 label 相當簡單, 只要判斷最後字元是不是 : 就行了:

static bool isLabel(const char *name){
    int len = strlen(name);
    if(len > 1  && name[len -1] == ':')
        return true;
    return false;
}

接下來延伸的問題就很多了:
我們需要一個新的資料結構紀錄 label, 此資料結構需要包含 label 的名稱以及對應的 instruction 之 pc 值:

//vm.h
typedef struct {
    char *name;
    int next_pc;
} vm_label;

再來是虛擬機器, 因為第一個功能區塊的 label 並不是指令, 我們需要用額外的空間把 label 記起來所以用 vm_label labels[LABEL_MAX_SIZE] 紀錄所有指令, 並且 int labels_count 紀錄有指令總數, 至於第二個功能區塊因為跳躍指令若使用 label 作為跳躍, 則要參照到這個 label 拿到它的資訊, 所以用 vm_operand *labels_ref[INSTS_MAX_SIZE] 紀錄所有參照到的 label , 並且用 int labels_refcnt 紀錄參照 label 的總數

struct __vm_env {
    vm_inst insts[INSTS_MAX_SIZE]; /* Program instructions */
    vm_label labels[LABEL_MAX_SIZE];
    vm_operand *labels_ref[INSTS_MAX_SIZE];
    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;
    int labels_count;
    int labels_refcnt;
};

觀察 Add label support 之後發現本來在判斷 operand 程式裡到若不是遇到 $ # 或是字串的以外情況都是當作 label (switch 的 default), 程式錯誤也有可能跳至 default, 所以需要參照到 label 時, 我做了一個 @ 辨識, 所以跳躍時會像這樣 jmp @label

static inline vm_operand make_operand(vm_env *env, char *line, const char *data)
{
    ...
    switch(data[0]){
    ...
    case '@':
    op.type = LABEL;
    op.label = strdup(data+1);
    break;
    ...
    }
}

如果不依賴 @ 識別 label 的話,該怎麼做?

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