計算機技術長篇分享

向 LoongArch 架構移植 Haskell GHC 編譯器

現如今,我們使用的計算機幾乎都是 X86 架構的,Intel 和 AMD 幾乎是普通消費者購買新計算機時的唯二選擇,可能會有少數的 Apple 用戶使用了配備 Apple Silicon 處理器的 Arm Mac,除此之外,似乎就沒有什麼別的體系架構可供選擇了。但實際上,無論是歷史還是現在,指令集的江湖都不是如此平靜的,過去有 MIPS 和 PowerPC 等等在遊戲機和伺服器等領域挑戰 X86 的地位,近有 RISCV 和 LoongArch 等試圖在嵌入式領域開闢新的領域,而這些挑戰者們都有一個共同點——都是 RISC(精簡指令集架構)處理器。

何謂 RISC?

要了解什麼是 RISC,首先就要清楚什麼是 CISC,因為 RISC 的概念正是從 CISC 延申出來的。上世紀 70 年代,Intel 推出了第一顆 X86 處理器——Intel 8086,作為 X86 系列的始祖,它的減配版本(地址線位數砍半)8088 被 IBM 選中,作為 IBM PC 的處理器,從此開啟了個人電腦和兼容機的傳奇,同一時期,Apple 也選中了摩托羅拉的 68000 CPU 作為第一台 Macintosh 的處理器,用來支持它引入的圖形界面。
Intel C8086 處理器
8086 與 68000 看似大相徑庭,但兩者的共同點就在於它們都是複雜指令集處理器(CISC),由於當時儲存器的價格普遍較高,編譯器也還不夠成熟,因此當時的軟體開發者往往傾向於手工編寫彙編指令,受此影響早期處理器的指令設計普遍比較複雜,並且採用了變長指令字的設計,用以支持各種複雜的操作,這樣可以減小程序的體積和程序員編寫指令的工作量,例如在 X86 中,就可以直接使用loop指令編寫循環,而無需手動編寫跳轉等指令。

然而,隨著時間踏入 90 年代,CISC 處理器暴露出了各種問題:一方面,過於複雜的指令集不利於引入流水線的設計,造成性能受到影響;另一方面,程序規模不斷增大,已經很難靠程序員去編寫所有彙編代碼了,編譯器也逐漸走向成熟,然而它們還是很難利用上 CISC 指令集上各種複雜的指令。受此啟發,人們搞出了精簡指令處理器(RISC),正如其名,它的指令相比 CISC 要簡單很多,一般有如下的特點:

  1. 指令字定長,通常是 32 位長,方便解碼器的設計
  2. 通用寄存器較多,例如 ARM 有 16 個寄存器,而 MIPS 有 32 個寄存器,減少訪存帶來的性能瓶頸
  3. 訪存由單獨的指令完成,普通指令只會對寄存器和立即數進行操作,簡化了處理器的設計
  4. 指令數量較少,語義單一,方便編譯器的實現

這些特點都給當時的 RISC 處理器帶來了巨幅的性能提升,RISC 處理器一時風光無兩,在遊戲機和伺服器等各種領域全面開花,就連身為 Wintel 聯盟一員的微軟都為 MIPS 處理器推出了 Windows NT for RISC 用來兼容當時在高端工作站領域攻城略地的 MIPS 處理器。

不過,正如《計算機體系結構》一書中所言,如今 RISC 與 CISC 的區別已經日漸消弭了,甚至很多 RISC 架構的指令集都不比早期 CISC 更簡單了:例如 PowerPC 的寄存器窗口和 MIPS 的延遲槽設計,有些甚至成為了性能提升的瓶頸,而傳統的 CISC 指令集則通過引入 uop 的方式在執行階段 RISC 化,話雖如此,新的指令集還是不斷出現,並且往往還是會採用 RISC 簡化的思想——例如 RISC-V 還有筆者本次要介紹的 LoongArch

LoongArch 簡介

龍芯架構 LoongArch 是一種精簡指令集計算機(Reduced Instruction Set Computing,簡稱 RISC)風格的指令系統架構。

這是《龍芯架構參考手冊 卷一:基礎架構》中對 LoongArch 給出的定義,LoongArch 是龍芯中科公司設計的一種 CPU 指令集架構,2020 年對外公開其存在,2021 年起公開出貨,在其 3A5000 CPU 產品開始搭載。龍芯之前的產品採用的是 MIPS 指令集,而隨著時代的變遷,MIPS 指令集的實控人幾經易手,就連 MIPS 公司自己都在 2020 年宣布放棄 MIPS,轉投 RISC-V 陣營,而龍芯的選擇則是另起爐灶——獨立開發 ISA。

當然,任何新指令集逃不過的一個問題就是生態,畢竟 X86 指令集能夠在今天仍然保持第一的佔有量,靠的就是其他指令集無法比擬的生態鴻溝,無數的軟體都是為了 X86 編寫的,並且很難簡單移植到其他的平台上,更遑論無數的閉源商業軟體了。而新架構要加強自身的生態,基本只有兩條路可以走:模擬(兼容層)和移植,筆者目前手頭就有一台 3A5000 開發機,並且正在從事有關軟體移植的工作。

移植指北

那麼,要如何讓軟體跑在 LoongArch 架構上呢?實際上,對於大多數軟體而言,並不需要做任何修改,在 C 編譯器,JRE 運行時,.NET 環境等基礎軟體基本完成適配之後,只需要將代碼重新編譯(Java 等甚至不需要編譯)運行即可,但對於一些依賴於底層彙編實現的軟體,則需要手動進行移植。

筆者在今年年初完成了對 Haskell GHC 編譯器向 LoongArch 架構的移植,並在 LLVM 和 GHC 開源社區中順利將移植代碼合併進了主線(雖然還差點鬧出點亂子),下面就以我當時的移植過程為例,展示將一個依賴於底層體系架構細節的應用移植到新架構上,都需要做哪些改動。

不過要注意的是,隨著 LLVM16 和 GHC 9.6.1 的發布,相關代碼已經集成進了主線源碼中,因此無需修改源碼即可直接進行編譯。

獲取源碼

首先,使用以下命令將 GHC LoongArch 分支克隆至本地

git clone -b loongarch-patch --recurse-submodules https://gitlab.haskell.org/lrzlin/ghc.git

GHC Unregisterised 版本編譯

根據 CLFS 文檔中的指南配置好交叉工具鏈後,找到源碼目錄中的ghc/m4/ghc_unregisteried.m4文件並將其中的loongarch64欄位刪除。

ghc/libffi-tarballsghc/libraries/ghc-bignum/gmp/gmp-tarballslibffigmp的壓縮包替換為支持 LoongArch 的版本。

之後回到GHC根目錄,輸入:

./boot
./configure CC=$SYSDIR/cross-tools/bin/loongarch64-unknown-linux-gnu-gcc --target=loongarch64-unknown-linux-gnu LD=$SYSDIR/cross-tools/bin/loongarch64-unknown-linux-gnu-ld

等待配置完成後,輸入如下命令進行編譯。

hadrian/build -j4

如果在編譯中碰到缺少ncurses庫的報錯,請自行編譯並將其安裝至交叉編譯工具鏈中。

編譯完成後,輸入:

hadrian/build binary-dist

打包二進位分發包,至此 GHC Unregisterised 版本編譯完成,可以將其拷貝至 LoongArch 架構機器安裝使用。

GHC Registerised 編譯

GHC Unregistered 即可滿足大多數情況下的使用需要,但如果需要更高的運行效率、更完善的庫支持和更完整的功能,則需要編譯 GHC Registerised 版本,由於該版本需要 LLVM 後端的支持,因此需要首先編譯支持 LoongArch 架構的 LLVM16。

註:以下操作均在 LoongArch 機器上完成。

首先需要安裝之前編譯完成的 GHC Unregisterised 版本,將 tar 包加壓縮之後,進入目錄並輸入如下指令安裝:

./configure
hadrian/build install

之後在 LLVM 官方網站或 Github Release 頁面下載 LLVM16 源碼,解壓至本地之後使用如下命令編譯安裝:

cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release \
              -DBUILD_SHARED_LIBS:BOOL=OFF -DLLVM_ENABLE_LIBCXX:BOOL=OFF \
              -DLLVM_LIBDIR_SUFFIX=64 \
              -DCMAKE_C_FLAGS="-DNDEBUG" -DCMAKE_CXX_FLAGS="-DNDEBUG" \
              -DLLVM_BUILD_RUNTIME:BOOL=ON -DLLVM_ENABLE_RTTI:BOOL=ON \
              -DLLVM_ENABLE_ZLIB:BOOL=ON -DLLVM_ENABLE_FFI:BOOL=ON \
              -DLLVM_ENABLE_TERMINFO:BOOL=OFF \
              -DLLVM_BUILD_LLVM_DYLIB:BOOL=ON \
              -DLLVM_LINK_LLVM_DYLIB:BOOL=ON -DLLVM_BUILD_EXTERNAL_COMPILER_RT:BOOL=ON \
              -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=OFF \
              -DLLVM_TARGET_ARCH=LoongArch -DLLVM_DEFAULT_TARGET_TRIPLE=loongarch64-unknown-linux-gnu
ninja && ninja install

由於 GHC 尚不支持最新的 LLVM 16 版本 因此需要對進行如下修改:

  • 將 ghc/llvm-passes 的內容修改為
    [
    (0, "-passes=module(default<O0>,function(mem2reg),globalopt,function(lower-expect))"),
    (1, "-passes=module(default<O1>,globalopt)"),
    (2, "-passes=module(default<O2>)")
    ]
  • 將 ghc/compiler/GHC/Driver/Pipeline/Execute.hs 第 902 行中的-tbaa刪除
  • 在 ghc/utils/genapply/Main.hs 中添加 #undef UnregisterisedCompiler

由於 GHC 在最新的主線移除了對 make 工具的支持,因此我們還需要編譯並安裝 cabal-install。請自行參考 Cabal Bootstrap 指南進行編譯,完成編譯後,請注意需要執行一次cabal update之後即可進入 GHC 根目錄並使用以下指令編譯安裝 GHC Registerised 版:

./boot
./configure
hadrian/build -j4
hadrian/build install

以上,就是一次「簡單」的移植過程。

參考文章:
https://bbs.loongarch.org/d/150-ghc-loongarch
https://blog.xen0n.name/posts/tinkering/loongarch-faq/

對這篇文章感覺如何?

太棒了
1
不錯
0
愛死了
0
不太好
0
感覺很糟
0

You may also like

Leave a reply

您的電子郵箱地址不會被公開。 必填項已用 * 標註

此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

計算機技術

區塊鏈:絕不只是加密貨幣

本文從時下熱門的加密貨幣——比特幣入手,介紹其背後隱藏的區塊鏈技術,由淺入深,介紹了區塊鏈技術的起源,基礎,應用,發展趨勢等,值得一看。
計算機技術

Firebug 與 DevTools 的集成

你可能已經聽說過我們對統一 Firefox 的本地開發人員工具(DevTools)和 Firebug 的努力。我們一直在努力地將的所有最喜歡的 Firebug 功能添加到本地 DevTools 中,使 […]
計算機技術

判斷Linux伺服器架構是32位/64位

作為一個 Unix 系統的新手用戶,我可以怎麼判斷我的 Unix 伺服器安裝的是32位或者64位的操作系統呢?你可以使用如下的命令來獲取關於 Unix 內核和 CPU 架構的信息。