--- tags: Research --- # Code-copying Dispatch ## Code-copying 動機 Code-copying 主要是為了降低一個 basic block 中的 instruction dispatch overhead 來勁一部提升 direct-threading 的表現. 實作概念就是將模擬各個指令之程式碼的起始記憶體位址與終止記憶體位址紀錄於陣列中,程式碼其實就是一串 machine code,接著建立一個可讀寫的記憶體空間,按照 basic block 中的指令順序,一一將指令之程式碼寫入該記憶體空間。執行時就直接執行該記憶體空間中的一連串 machine code。 ![](https://i.imgur.com/0SNivqF.png) 通過 code-copying 來的第一個好處就是減少 jump 指令次數,而且出現在這些 copying code 中的 jump 的指令更好預測。 另一個好處則和 cache locality 有關,過往在非連續的記憶體位址中調用某一指令之程式碼,由於 cache 大小有限,當兩段程式碼之記憶體位址格的太遠時,就會發生 cache miss。然而,在 code-copying 機制中,一連串被調用的指令程式碼記憶體位址都是連續的,這樣的情況大大降低 cache miss 的機率。 ## Code-copying 實作 ### 1. 建立 memory page 在 decode 後可以得知 basic block 中含有幾道指令,並以此算出這個 basic block 需要的 準備幾個 memory page 來將指令程式碼複製進去。 ```c static void *malloc_exec(uint32_t page_count) { void *block = mmap(0, getpagesize() * page_count, PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); return block; } ``` ### 2. 紀錄各個指令之程式碼的起始記憶體位址與終止記憶體位址 陣列 `handle_ENTRY` 紀錄了所有程式碼之起始記憶體位址,陣列 `handle_END` 則紀錄終止記憶體位址。 ```c static const void *handle_ENTRY[] = { #define _(inst, can_branch) [rv_insn_##inst] = L(inst, ENTRY), RISCV_INSN_LIST #undef _ }; static const void *起始記憶體位址[] = { #define _(inst, can_branch) [rv_insn_##inst] = L(inst, END), RISCV_INSN_LIST #undef _ }; ``` ### 3. 將指令之程式碼寫入該記憶體空間 按照 basic block 中的指令順序,一一將指令之程式碼寫入配置好的記憶體空間中。 ```c for (uint32_t i = 0; i < block->n_insn; i++) { ir = block->ir + i; memcpy(PC, handle_ENTRY[ir->opcode], handle_SIZE[ir->opcode]); CAL_PC(handle_END[ir->opcode], handle_ENTRY[ir->opcode]) } ``` ### 4. 寫入回傳指令 程式碼都寫入記憶體空間後,將回傳指令寫於最後面,且回傳指令 size 依照硬體架構而定。 ```c memcpy(PC, &&op_halt_ENTRY, HALT_SIZE); ``` ### code-copying 中呼叫外部函數 在呼叫外部函數時,必須利用`絕對地址`來呼叫,相對地址是相對於目前程式碼所在位置,和 code-copying 後的程式碼所在位置並不同。如果用相對地址來呼叫,會跑到不對的記憶體位置執行,會造成 segmentation fault. ## issue 1. gcc-11 以及 gcc-11 以前版本 inline assembly 只能用相對地址來呼叫外部函數 2. clang 在執行寫入的回傳指令時會出錯 3. clang 對於 x86 架構的 inline assembly 支援度不高(無法指定 x86 暫存器) 4. 壓縮指令還有呼叫 exception handler 的部分,這些指令有呼叫到外部函式,因為 push 的 return address 是相對位址,相對程式碼原本所在的地方,這個位置和 code-copying 後的程式碼所在位置並不同,會造成 segmentation fault,目前大部分問題都與此相關。