# Compiler, Assembler and Interpreter 了解


## C 語言

Preprocessing: 預處理，把程式碼所有用 # 開頭的指令進行替換

1. C ISO 標準定義，所謂的 翻譯階段 (Phases of Translation)。C 語言的編譯過程在標準中被拆解為 8 個邏輯階段，而「預處理」主要涵蓋了前 4 個階段
    1. Phase 1-3： 處理字元映射、將連行符號（\）合併、把註解換成空格
    2. Phase 4： 執行所有 預處理指令（也就是你看到的 # 開頭的東西）並進行 巨集展開 (Macro Expansion)。

最後產出了 `.i` 檔案，可以參考[C语言的翻译阶段](https://blog.yurich.me/posts/translation-phases-in-c/)。

再來就是編譯，正式編譯，也就是第七階段，編譯器會做語法分析，語意檢查，並將程式碼轉換成目標碼，基本上就是兩步驟。

* Compiler 負責把 C 變成 .s (Assembly)
* Assembler 負責把 .s 變成 .o (Object code)

這邊的 Object Code 其實就是二進位的 Machine Code，但有特定格式，像是在 Linux 是 ELF。

ELF 裡面有像是:

* .symtab：符號表 (Symbol Table): 這張表紀錄了這個檔案中定義或引用的所有「名稱」
* .strtab：字串表 (String Table)
* .rel.text / .rela.text：重定位表 (Relocation Records)

最後一步就是 Linker 針對 Object Code 裡面的重定位表，把像是 printf 函式真正的 address 塞進來，最後產生像是 `a.out` 類似的執行檔。

這類執行檔還是 ELF 格式，他把原本散落在各個 .o 裡的 .text（程式碼）通通黏在一起，變成一個巨大的區塊。

然後多了像是：

* Program Header (程式頭部)：
  * Section Header（Linker 看的）： 告訴 Linker 哪裡是符號表、哪裡是重定位表
  * Program Header（OS 看的）： 告訴 OS「請把這 1MB 的資料載入到記憶體位址 0x400000，並給它『唯讀+執行』的權限」

---

最後的 ELF 執行檔，可以給 OS 直接跑，OS 會用 Kenre Loader 讀取此執行檔，然後把檔案的內容 Map 到記憶體。

在這執行的時候假如有用到動態函式庫，Kernel 會先啟動一個叫做 `ld-elf.so` 的小程式，同樣進行 Link。

所以我們可以把 OS 當成一個 ELF Runtime，他有 Loader 載入 ELF 程式，提供[執行環境](https://en.wikipedia.org/wiki/Execution_(computing))，像是：

* 記憶體管理 (Memory Management)
  * 它必須定義 Stack（堆疊） 如何增長、Heap（堆積） 如何分配
  * 對於 C 是 OS 核心 and libc，OS 核心管理 Paga Table 等
* 執行上下文控制 (Execution Context)
  * 管理 Program Counter (PC)、Stack Pointer (SP) 和暫存器 (Registers)
* 執行緒與排程 (Threading/Scheduling)
* System Call Interface

{{< image
    src="image.png"
    alt="C 語言編譯流程"
    caption="C 語言編譯流程"
>}}

常見的 C compiler 叫做 GCC ，各家公司也會實做自己的 C compiler (Visual studio C/C++ …)，也有人對 C 語言專門設計一個直譯器，叫做 Ch。

### 組譯器

組譯器其實也是一種編譯器，只是因為強調是組合語言當作輸入，並且組譯器多半比編譯器還要簡單。

有些編譯器看起來是直接輸出機械語言，其實多半也是先輸出組合語言，再在背後執行組譯器。

## Python

先來定義 Interpreter 是什麼：

* Compiler 是先把程式碼編譯成機器語言，之後一次執行完全部
* Interpreter 則是在執行時是一邊解釋成機器語言，一般執行

實際看 Python 他運作是，先用 Compiler 編譯成 Python bytecode，然後讓 Bytecode 在 Python VM 上面直譯，邊翻譯邊執行。

注意這邊多了一個名詞叫做 VM，他可以看作一種 Runtime，但他是一種模擬硬體行為的軟體層，它在真實硬體與程式之間多墊了一層。它不直接讓 CPU 跑機器碼，而是定義了一套自己的「虛擬指令集」（如 Python Bytecode）。

所以他要像是 OS Runtime 一樣，提供像是：

1. Loading：載入
2. Execution Context：PVM 維護一個 PyFrameObject 的鏈結串列。每個「框架」裡都有自己的 Bytecode 指針（模擬 PC）和局部變數表。這讓你可以在 Python 拋出異常時看到完整的 Stack Trace，這其實就是 VM 在讀取它維護的上下文
3. 排程：雖然 Python 使用系統執行緒，但為了保護內部資料狀態，它加了一個 GIL (Global Interpreter Lock)。這個鎖就是 VM 層級的「簡易排程器」，決定哪一個執行緒現在可以動用 VM 的資源
4. Memory 管理：不太需要 Page Table，是比較像是 GC or Object Allocator
5. Call Stack：Virtual Stack Frames: PVM 自己維護一個 Call Stack 來模擬函式跳轉
6. Linking：執行時才去 Namespace (dict) 查找變數或函數名稱
7. System Call：提供封裝過的 API（如 os.open()）來對接底層 Syscall

{{< image
    src="image-1.png"
    alt="Python 語言編譯流程"
    caption="Python 語言編譯流程"
>}}

注意這邊是每個不同的 Python Interpreter 實作會產生自己的 Bytecode，所以你不能把 CPython Bytecode 跑在 RustPython VM 上面。

Python 成功的原因之一是它有 NumPy、TensorFlow 等極強的 C 套件。這些套件是直接跟 CPython 的內部結構（C 語言層級）溝通的（就像 ELF 有 ABI 一樣，Python 也有 Stable ABI (PEP 384)。它確保編譯好的 C 擴充套件在升級 Python 小版本時不需要重新編譯。）

## Java

Java，類似 Python，他有自己的 VM 叫做 JVM，但他有一份極其嚴謹、厚重的 JVM Specification。不論是 Oracle、IBM 還是 Amazon 實作的 JVM，都必須 100% 遵守這份規格。

Java 的目標是成為一個虛擬作業系統。它不只跑 Java 語言，還能跑 Kotlin, Scala, Groovy。這些語言都編譯成同一套標準 Bytecode。JVM 不關心你是哪種語言，它只負責把標準 Bytecode 跑得飛快。

所以有了規格，不管是哪個語言，哪個 Runtime 的 Compiler，產生的 Bytecode 只要符合規格，可以在不同人的 JVM 上面跑。你在 Mac 上用 OpenJDK 編譯出來的 `Main.class`，可以直接丟到一台執行 Windows 的伺服器上，用 Oracle 的 JVM 跑，結果保證一模一樣。

使用的流程是：

1. 先編譯成 Jave bytecode (`.class`)
2. 直譯，逐行讀取 Bytecode 並執行
3. 當 JVM 發現某個方法或迴圈跑了非常多次（稱為 Hot Spot 熱點），它會覺得：「一直直譯這段太慢了！」。背景執行緒會把這段 Bytecode 直接編譯成該平台（x86/ARM）的機器碼。下次再執行到這裡，CPU 會直接跑機器碼，速度跟原生 C++ 幾乎沒兩樣。

{{< image
    src="image-2.png"
    alt="Java 語言執行流程"
    caption="Java 語言執行流程"
>}}

## WASM

他把 WASM bytecode 當作是一種平台，所以不同語言的編譯器都可以編譯成他，除了自己的 WAT (WebAssembly Text format)，可以當成 WASM Bytecode 的處合語言。

WASM 類似 Java，同樣有自己的標準 for WASM bytecode (`.wasm`)，來自 W3C：

1. Wasm 核心規範 (WebAssembly Core Specification)：它規範的是 `.wasm` 檔案本身的結構
   1. 二進位編碼： 檔案開頭必須是 `0x00 0x61 0x73 0x6d` (即 `\0asm`)
   2. 指令集 (ISA)： 例如 `i32.add` 的 Opcode 是 `0x6A`
   3. 模組結構： 哪一段是定義變數（Globals）、哪一段是程式碼（Code Section）
   4. 驗證規則： 確保程式碼不會做出越權存取記憶體等危險行為
2. WASI (WebAssembly System Interface)：讓 WASM bytecode 有同樣的 API 能執行

如果你把 `.wasm` 看成一個 ELF 執行檔：

1. Wasm Bytecode 就像是 ELF 裡的機器碼指令
2. WASI 就像是 系統調用 (Syscall) 表 或 libc 的標準介面

---

WASM 也是有自己的 VM，並且通常提供兩種模式執行：

1. JIT 模式，類似 Java
2. AOT (Ahead-of-Time)：在程式執行之前，先用編譯器把 `.wasm` 完整地轉成該平台的原生機器碼 (Native Machine Code)，並封裝成一個特殊的執行檔（在 WasmEdge 中通常是 `.so` 或特定的 AOT 格式）。就算編譯成機器碼，還是要在 Runtime 上面執行，不能直接在 OS 執行。

### WASM Sandobx

另外一個特別的是 WASM 有做 Sandbox。

<https://www.ithome.com.tw/news/153022>

## JavaScript (V8)

JavaScript 雖然表面上是腳本，但 Chrome 的 V8 引擎會進行極其複雜的編譯優化。

## Deep Learning Compiler

...

## Reference

* [編譯器(compiler) 組譯器(assembler) 直譯器(interpreter) 虛擬機(virtual machine) 介紹](https://cozy-kola.medium.com/%E7%B7%A8%E8%AD%AF%E5%99%A8-compiler-%E7%B5%84%E8%AD%AF%E5%99%A8-assembler-%E7%9B%B4%E8%AD%AF%E5%99%A8-interpreter-%E8%99%9B%E6%93%AC%E6%A9%9F-virtual-machine-%E4%BB%8B%E7%B4%B9-10e56bf575e2)

