# A09: jit-compiler ## 預期目標 * 學習設計 JIT 編譯器 * 運用計算機組織的背景知識,進行深入效能評估和提出改善方案 * 延展程式語言,提供 concurrency 能力 由於我之前有做過這個作業的 pattern 優化了,所以這次重心放在 parallel 和 concurrency >>中英文字間請以空白隔開 >>[color=red][name=課程助教] ## JIT - Phase 1: create machine code at program run-time. - Phase 2: execute that machine code, also at program run-time. 看了一點參考[1]和參考[2],JIT 是先把目標程式碼(e.g. brainf*ck)編譯成 machine code 後,再放入程式記憶體執行,再這次的作業中,使用的是 JIT 是 Dynasm 。 這邊貼出參考[1]裡的部份程式碼 ```clike= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> // Allocates RWX memory of given size and returns a pointer to it. On failure, // prints out the error and returns NULL. void* alloc_executable_memory(size_t size) { void* ptr = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ptr == (void*)-1) { perror("mmap"); return NULL; } return ptr; } void emit_code_into_memory(unsigned char* m) { unsigned char code[] = { 0x48, 0x89, 0xf8, // mov %rdi, %rax 0x48, 0x83, 0xc0, 0x04, // add $4, %rax 0xc3 // ret }; memcpy(m, code, sizeof(code)); } const size_t SIZE = 1024; typedef long (*JittedFunc)(long); // Allocates RWX memory directly. void run_from_rwx() { void* m = alloc_executable_memory(SIZE); emit_code_into_memory(m); JittedFunc func = m; int result = func(2); printf("result = %d\n", result); } ``` 在上面的程式碼當中,需要先產生可讀可寫可執行的記憶體(藉由mmap搭配一些指定的flag),接下來將編譯好的機械碼(這邊省略編譯的步驟)複製到之前產生的記憶體空間裡,這樣一來,這裡的機械碼就可以被執行囉! 最後將指定 function 到記憶體的位址( function pointer 概念),就可以被呼叫來執行。 ## 編譯與測試 在 clone 下來之後嘗試 make,不過少了幾個套件,先安裝好再說 ```c lua dynasm/dynasm.lua -o jit-x86.h jit-x86.dasc make: lua: Command not found make: *** [jit-x86.h] Error 127 ``` 需要先安裝lua ``` Package lua is a virtual package provided by: lua5.1:i386 5.1.5-5ubuntu0.1 lua5.1 5.1.5-5ubuntu0.1 lua5.2:i386 5.2.3-1 lua50 5.0.3-7 lua5.2 5.2.3-1 You should explicitly select one to install. ``` 選擇安裝5.2 ``` jit-x86.dasc:4: error: cannot load module: dynasm/dasm_x86.lua:31: module 'bit' not found ``` 安裝lua-bitop ``` /usr/include/features.h:374:25: fatal error: sys/cdefs.h: No such file or directory ``` 安裝libc6-dev-i386 ``` make: arm-linux-gnueabihf-gcc: Command not found ``` 最後一個了,安裝gcc-arm-linux-gnueabihf 終於OK啦! ### 第一次測試 由於我的電腦是64bit的系統,那就跑 ==make bench-jit-x64== 吧 ``` Executing Brainf*ck benchmark suite. Be patient. progs/awib.b GOOD 73.1ms progs/mandelbrot.b GOOD 3025.4ms progs/hanoi.b GOOD 7669.4ms ``` ## 特定pattern 優化 ## Concurrency 因為interpreter支援brainfuck平行化是最好寫的,所以打算先從interpreter開始著手 ### interpreter的concurrency 目前想讓inerpreter版本支援concurrency,這樣我們就必須先定義一些符號來產生thread,中止thread以及同步 ==*== : 創造一個 thread ,採用類似 clone 的方式,會複製 main thread 的 tape,並開始執行後面的運算直到碰到 ==&== ==&== : 取諧音 end,用來表示 thread 結束位置 ==|== : 等待 thread 執行完畢,用來同步 紀錄一下,在 coding 過程中,其中有個部份如下面的寫法一樣 ```clike= case '*': // create a thread pthread_t threadid; ``` 在編譯的時候遇到下列 error ``` a label can only be part of a statement and a declaration is not a statement ``` 在C語言裡,接在 label 後面的第一行程式碼必須是statement ,而 declaration 不算是 statement,因此編譯器不給過,那我們可以加一個 empty statement 在 declaration 前面就可以了 ```clike= case '*': // create a thread {} pthread_t threadid; ``` > 不過後來我已經捨棄這種寫法[name=林哲亘] ### 範例 ``` ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.*<<+++++++++++++++.>.+++.------.--------.>+.>.&| ``` 以 helloworld 作為例子,在 main thread 執行完 ==Hello== 的輸出後,關鍵在於後面的*符號,我們的 C interprter 在直譯到這個地方時,會使用 pthread_create() 來產生新的執行緒,新的執行緒一樣會繼續直譯 brainf*ck 程式碼,而原本的執行緒會跳到&符號的位置,再繼續直譯。 回到剛剛 helloworld 的例子,產生的 thread 用來輸出後半段的 ==world!== ,這時候的原本的 thread 直譯到了|符號,這個用來等待產生出來的執行緒結束(利用pthread_join()),才會繼續,而 helloworld 的例子則是因為後面沒有其他的程式碼,因此直接結束。 特別一題的是我採用的是用 C clone 的概念去產生 thread,也就是說,會複製原本的 tape 讓新生的 thread 去使用,等同於共享到創造 thread 之前的變數( tape ),但是 thread 的 pc 則是一樣的,換句話說,thread 是從被創造的那一個指令接續之後的動作,不像 pthread 產生新的 thread 後,會跳到其他的 function 位置後才繼續執行。 目前不支援 thread 裡面又創造一個新的 thread,在直譯的時候會跳出 error message ## Reference 1. http://eli.thegreenplace.net/2013/11/05/how-to-jit-an-introduction 2. http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html