Kotlin 2.0 降臨,迎來嶄新的 K2 Compiler(下)

Andy Lu
7 min readOct 2, 2024

上一篇文章介紹了 Kotlin 在跨平台的佈局,開發者若想完全使用 Kotlin 開發跨平台專案,只需要選擇 Compose Multiplatform 加上 Kotlin Multiplatform(KMP) 就能實現這個願望,其中 Compose Multiplatform 負責共享 UI 邏輯,KMP 共享商業邏輯。若想使用原生的 UI,也可以只使用 KMP 共享商業邏輯。

話說回來,為什麼現在有那麼多跨平台解決方案?跨平台解決方案最重要的一個要素就是「只需要維護一個專案,就能依照不同目標平台編譯在各平台上。」無論是 Mobile(Android、iOS)、Web 都能共用程式碼。使用跨平台解決方案進行開發的好處,開發時間就可以縮短,人力也可以減少。

但是,同一個專案要如何編譯上不同平台?最重要的就是編譯器(Compiler)。編譯器,其功能就是將高階語言所編寫的程式碼轉換成機器語言,換句話說,就是讓我們寫的 Kotlin 程式碼,轉換成機器能看得懂的語言,但是由於不同平台上的機器語言都不盡相同,所以若想把 Kotlin 程式碼編譯成不同平台上的機器語言,最大的困難點就是在編譯器本身。

Kotlin 在初期的版本,並不是為了日後能方便開發不同平台,而是希望能快速開發。但隨著 Kotlin 的發展,目標平台越來越多,假如每次新增目標平台時,就需要新增不同平台的編譯器,而如果新增新的語法,就必須要修改各個編譯器,如此一來當需要支援新的語法、新的目標平台,Kotlin 團隊就需要大量的人力修改,當然 Kotlin 團隊也意識到這個問題,於是在很早的版本開始就開始進行編譯器的改版。 經過了多年的發展,新一代的編譯器(K2 Compiler)終於與 Kotlin 2.0 一起現身,要完整了解 K2 Compiler,首先我們要先從編譯器開始認識起。

編譯器(Compiler)

前面提到,編譯器的作用是將程式碼編譯成不同平台的機器語言。示意圖如下:

在 KMP 中,因為可以將程式碼編譯成不同平台的機器語言,所以示意圖如下:

從上方的示意圖可以發現,Compiler 內部會依據不同的目標平台,而編譯成不同的機器語言。以目前 KMP 所支援的編譯器來說,可以分為四種編譯器: Kotlin/JVM、Kotlin/JS、Kotlin/Native 以及 Kotlin/Wasm。

KMP 已經在 2023 年 十一月推出了正式版,上述的編譯器除了 Kotlin/Wasm 尚在 Alpha 階段,其餘都已經進入 Stable (穩定版)。

Frontend/ Backend Compiler

深入編譯器的內部,我們可以將其分為兩個部分:一是前端編譯器(Frontend Compiler)、另外則是後端編譯器(Backend Compiler)。 這邊提到的前後端並不是指網頁上所代表的前後端,小心不要搞混了。

前端編譯器負責將程式碼轉換成語法樹以及語意訊息,主要是將高階語言轉換成低階的語法,讓後端編譯器能轉換成機器語言。

若再更深入研究,前端可以分為兩個部分,語法分析器(Parser)與語意分析(Semantic analytics),其中語法分析器是將程式碼轉換成語法樹,而語意分析則是分析裡面的語意,並存在另外一個資料表(語意訊息)中。

後端也分為兩個部分,前面是中間語言產生器(Intermediate code generator),將前端編譯器產生的語法樹與語意訊息轉成中間語言(Intermedia Representation — IR),接著是機器語言產生器(Machine code generator),將其轉換成機器語言。 其中,中間語言的目的是將不同平台的細節抽象化,將不同後端編譯器標準化,如此讓相同的前端編譯器能支援不同的後端編譯器。更直白的說,就是共用不同的後端編譯器,讓未來的擴展更方便。有了 IR,轉換成機器語言的過程就會更高效。 不過,雖然後端編譯器包含 IR 是有好處的,但是在 Kotlin 開發初期並未針對 Kotlin/JVM 以及 Kotlin/JS 加上 IR,原因無他,就是因為想要快速的推出。

New Frontend / Backend Compiler

前面談到 Kotlin 在一開始開發時,並沒有針對 Kotlin/JVM 以及 Kotlin/JS 加上 IR。當時後端編譯器的內部圖如下,只有 Kotlin/Native 編譯器包含 IR :

那麼,K2 Compiler 的第一步,就是將 Kotlin/JVM 以及 Kotlin/JS 加上 IR 層,並且這層 IR 是可以在不同編譯器上都能共用,於是目前的後端編譯器示意圖如下,Kotlin 分別 在 1.5 版以及 1.8 版,發布 JVM IR Backend 以及 JS IR Backend 穩定版:

加上共用的 IR 層,簡化不同平台的後端編譯器開發。加速了跨平台編譯器的進展。

介紹完新後端編譯器後,接著就是前端編譯器。從前面的介紹我們得知,經過前端編譯器後,我們會得到語法樹以及語意訊息,編譯器需要知道程式碼的內容,需要在兩個資料結構中來回的查詢。而新的前端編譯器將兩個部分整合在一起,並且包含其他編譯器需要的內容。而這個新的編譯器稱為 Frontend IR Compiler。

所以我們可以簡單用一句話來形容新前端編譯器的改動「合而為一」。新的前端編譯器架構,將所有編譯器需要知道的內容包在一個資料結構內,之後如果有新增語法,使用新的編譯器架構就能快速的開發,提高了效能,也增加了與 IDE 整合的性能。

整合上述對 K2 Compiler 的介紹,我們可以得知 K2 Compiler 改寫了編譯器內部的兩的部分(前端與後端),新前端編譯器將所有資訊整合在 FIR 內,而新後端編譯器新增加了 IR 層。由上面的介紹我們便可以了解,如此的改動,可以讓新語法的實作更方便,跨平台的編譯器開發更快速。當然,效能會更好。

結論

Kotlin 2.0 帶來了全新的 K2 Compiler,K2 Compiler 大幅度重寫了編譯器的前端與後端,因應未來程式語言目標與挑戰。 新前端編譯器因為擁有更多程式語言的資訊,所以型別推斷能夠更正確,而 IDE 的效能也可以更好,支援新語法的速度也會更快。 新後端編譯器,讓所有平台共用 IR 層,如此就可以共用相同的編譯器程式碼,開發不同平台的編譯器就可以更快速。 相信在 K2 Compiler 穩定版推出後,Kotlin 在跨平台的進展能更快速,更順暢。

--

--

Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.