Nibble: 用 C 語言實現的極簡主義 LLVM 前端

現代編譯器構建的領域通常以龐大、複雜的框架和多輪處理(multi-pass)架構為特徵。然而,由 glouwbug 開發的 Nibble 專案挑戰了這種極簡主義,透過大約 3,000 行的 C 語言實現了一個單輪處理(single-pass)的 LLVM 前端。藉由剝離傳統編譯器組件,如抽象語法樹(AST)和動態記憶體配置(malloc),Nibble 為語言設計與架構中的極致極簡主義提供了一個極具說服力的案例。

極簡主義架構

Nibble 的主要目標是實現從原始碼到 LLVM IR 的高效單輪轉換。大多數傳統編譯器遵循詞法分析、解析、遞迴下降生成 AST,然後遍歷該 AST 以生成程式碼的流程。Nibble 繞過了整個過程。

透過消除 AST,編譯器不需要為其正在解析的語言構建中間表示。相反,它直接從處理解析的同一輪處理中生成 LLVM IR。這種方法顯著降低了開銷的複雜度以及編譯器本身的記憶體管理足跡。

關鍵技術限制

為了進一步挑戰極簡主義的界限,作者實現了以下限制:

  • 不使用 malloc:編譯器在過程中避免了動態記憶體配置,這確保了確定性的記憶體足跡,並避免了與動態配置相關的碎片化問題。
  • 無外部依賴:該專案使用純 C 語言編寫,僅依賴語言的核心函式庫,確保了高移植性與輕量級的足跡。
  • 單輪處理:轉換發生在對同一份原始碼的單次遍歷中,這是處理轉換過程的一種極高效方式。

社群回饋與觀察

雖然該專案受到了社群的廣泛好評,但 Hacker News 社群也提出了一些技術疑問並要求提供更多文件。

語言規範

主要的批評之一是缺乏全面的語言規範。使用者指出,雖然該專案是一個可運行的實現,但語言的其他功能——例如 defer 關鍵字和記憶體管理——對於閱讀原始碼的人來說仍然充滿謎團。正如一位使用者 @childintime 所說:

"喜歡這個。但沒有關於語言的解釋。例如我檢查了那兩個 main.n 並沒看到 defer,而記憶體管理仍然是一個謎。"

效能與擴展性

社群對於 IR 生成的擴展性感到好奇。由於編譯器是單輪處理且缺乏 AST,某些可能需要對同一段程式碼進行多次遍歷的高階優化和複雜語言功能會受到天生的限制。這使得其他人想知道,隨著語言複雜度的增加,IR 的擴展能力能達到多遠。

結論

Nibble 是對挑戰編譯器前端限制能力的證明。透過證明一個功能完備的 LLVM 前端可以在沒有 AST 的情況下實現,並採用單輪處理架構,它提供了一個關於簡潔性的寶貴教訓。對於對編譯器構建感興趣的人來說,Nibble 是一個非常棒的實例,展示了一個真正交付產品的專案,超越了許多副業專案僅停留在「僅有 README」的階段。

Sources