# PoCL and OpenCL
重新撰寫文件使其更具有結構,以便知識的傳遞。
* PoCL 編譯
* LLVM 與 PoCL 的關聯
* front-end (`.cl -> .ll`)
* Optimize (`.ll -> .ll`)
* back-end (`.ll -> .s -> .o`)
* PoCL for RISC-V
* OpenCL and PoCL
* PoCL offline compiler
* standalone OpenCL runtime for PoCL kernel function
## PoCL 編譯
最基礎的架構下,你需要一個已經 build 好的 LLVM (最好是以 Debug mode 編譯),以及一個 PoCL 的 source code。
> PoCL 需要 LLVM Debug infomation 去保存編譯過程中的資訊,因為 PoCL 是將 LLVM 當作一個 Library 去使用。當然, LLVM debug build 跟 release build 之間有著巨大的差異。不論是最後生成的 lib 大小或者是 linker 的速度。
* LLVM 的 target 會決定 PoCL 的 target
* 用來編譯 PoCL 的 compiler 的 target 會決定 PoCL 的 target
針對以上這兩個條件下,我們可以嘗試去編譯出下面三種不盡相同的 PoCL 以便實驗的進行。
### X86
> 詳看包好的 script
### RISC-V cross compile
> 詳看包好的 script
### RISC-V native
> 詳看包好的 script
## POCL Usage
### Native
PoCL 預設會運行的環境是一個 homogeneous system。也就是說 Host 與 device 的架構是相同的。
這個狀況下我們可以藉由 OpenCL API 完成 OpenCL program 的編譯與執行。
```
// 舉例來說,現在有一個 vec_add.c 會去讀取 vecadd.cl
// 並且準備好 input 與 config
gcc vec_add.c -o vecadd -I<pocl/include/path> -L<pocl/library/path> -lOpenCL
```
最後執行 vecadd 這個執行檔,PoCL 便會完成
* .cl -> .ll -> .ll -> .s -> .o -> .so then cache the result in cache directory
* call dlopen and dlsym to find the requested kernel function
* host code invoke kernel function with specify argument
### Non-Native
如果不巧,現在要執行的環境是 heterogeneous system。Host 與 device 使用不同的架構。
> 舉例來說,host 是 x86 而 device 是 RISC-V
這個時候就需要一些流程上的改進才能順利執行這個 OpenCL 程式。
* 使 PoCL 產生出 kernel object file
* 產生出與 kernel 相同的 host code
* 結合以上兩者編譯後傳送給 device 執行後將結果傳回 host
目前這個版本僅能完成第一點與小部份的第二點。
運作的邏輯如下
* 利用 PoCL 原本就有的 cache system 取出編譯完成的 kernel object 放置於當前目錄
* 手寫一份以特定 kernel function 為目標的 host code
* 以 device 的 cross compiler 編譯後給模擬器執行
## LLVM 與 PoCL 關聯
PoCL 會將 LLVM 作為一個 library 去使用,所以不會直接去呼叫包裝好的執行檔,而是去呼叫 LLVM/clang 內部實作的 function。
### Front-end
詳看 `pocl/lib/CL/pocl_llvm_build.cc`
```
// In function
// int pocl_llvm_build_program(...
// ss store option as string
std::stringstream ss;
// append our option into ss
ss << our_option;
```
這個部份是決定
* OpenCL 相關 option
* `.cl -> .ll`
### Optimize
詳看 `pocl/lib/CL/pocl_llvm_wg.cc`
* 這個用的 PassManager 實際上是 legacy::PassManager
* 這裡運作的方式是先用 string 去標注 pass name,再用 PassRegistry 去查找這個 LLVM pass 的 instance
* O3 系列 optmization pass 被標注為 STANDARD_OPTS 後從 PassManagerBuilder 去建構一系列的 Optimization
```
// In function
// static PassManager &kernel_compiler_passes(...)
```
### LLVM CommandLine
詳看 `pocl/lib/CL/pocl_llvm_utils.cc`
* 它會在初始化 LLVM 的同時賦予值給相對應的變數
* 還有從 LLVM 取得 generic 的 pass
```
// In function
// void InitializeLLVM()
O = opts["scalarize-load-store"];
assert(O && "could not find LLVM option 'scalarize-load-store'");
O->addOccurrence(1, StringRef("scalarize-load-store"), StringRef("1"),
false);
```
### Back-end
詳看 `pocl/lib/CL/pocl_llvm_wg.cc`
```
// In function
// static TargetMachine *GetTargetMachine(cl_device_id device, Triple &triple)
TargetMachine *TM = TheTarget->createTargetMachine(
triple.getTriple(), MCPU, StringRef(""), GetTargetOptions(), Reloc::PIC_,
CodeModel::Small, CodeGenOpt::Aggressive);
```
在進行`.ll -> .s`與`.s -> .o`的階段時,PoCL 都會創建一個新的 TargetMachine 去進行這個任務。這個部份主要是可以讓我們任意的在 TargetMachine 中去新增各種 Target feature 的參數已達成去操控 Backend 行為的目的。
舉例來說,我們要手動開啟 V extension 的指令集的話,我們可以新增一個 Target feature `+v`
```
TargetMachine *TM = TheTarget->createTargetMachine(
triple.getTriple(), MCPU, StringRef("+v,+i,+m,+a,+f,+d"), GetTargetOptions(), Reloc::PIC_,
CodeModel::Small, CodeGenOpt::Aggressive);
```
## PoCL for RISC-V
## PoCL offline compiler
* offline compiler 的目的是產生出一個執行檔可以將 `.cl -> .o`
* 並且可以藉由 option 去開關客製化的功能
* 例如新的 LLVM Pass
* 例如啟動公司私底下做的加速指令
* offline compiler 基於 PoCL 原始就有提供的 poclcc 並且進行一些改造

* 主要分成兩個部份,並且藉由 poclcc 連接起來
* Option parser
* 從 opencl-clang 拔過來的
* 基於 LLVM tablegen 的 option parser
* OpenCL API
* 讀取特定的 .cl
* 呼叫綁定的 LLVM 進行後續的 codegen 處理
* 連接兩者的是使用 environment variable
* 會這個做的原因是因為 PoCL 原本就使用很多 environment variable 去控制 PoCL 在 codegen 時的行為
### Usage
```
poclcc -s <OpenCL file path>
```
* 會產生出一個以 OpenCL kernel function 為名的 object file
* 如果在同一個 OpenCL file 中有多個 kernel function,就會產生數個相對應的 object file
*
### options parser (OpenCL-clang)
* 這個專案是由 intel 所維護
* 目的主要是新增一個專門給 OpenCL 用的前端 parser
* 運作的方法為
* 撰寫 llvm tblgen 檔案
* 產生 marco
* 用 marco 產生更多 marco
* 用 switch 去判斷被啟動的是那一些 option
* 根據 option 的不同,更新相對應的 environment variable
### OpenCL API (pocl)
* PoCL 實作了相當完整的 OpenCL API
* 在 offline compiler 可以直接沿用
* 這個部份我們需要修改的有兩個
* 取得 options parser 維護的 environment variable 並且用於 codegen 流程中
* 改造 cache 機制以便使用者取得編譯完成的 object file