计算机技术长篇分享

向 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 架构的信息。