Julia 在 RISC-V (Linux) 上的编译与交叉编译指南
【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/julia
本指南以 Julia 官方开发文档 doc/src/devdocs/build/riscv.md 为主体,系统讲解如何在 64 位 RISC-V(RV64)Linux 设备上原生编译 Julia,以及如何在 x86 等更快的主机上通过交叉编译产出 RISC-V 版本。读完本文,你将掌握MARCH/MCPU/JULIA_CPU_TARGET等核心构建变量的正确设置方式,并能独立搭建 riscv-gnu-toolchain + QEMU 的交叉编译环境,完成 Julia 在如 StarFive VisionFive2 等开发板上的完整构建。
RISC-V 支持现状与前提
Julia 目前对运行 Linux 的 64 位 RISC-V(RV64)处理器提供**实验性(experimental)**支持。这意味着:
- 构建与运行链路已经打通,但某些功能仍可能存在缺陷或性能问题;
- 社区维护着一份针对 RISC-V 的已知问题列表(对应 Julia 官方问题追踪器中的
system:riscv标签),在动手之前可以先查阅; - 官方建议:如果遇到困难,在提交 issue 时务必附上
cat /proc/cpuinfo的输出,以便维护者确认你的 CPU 型号与 ISA 扩展情况。
与 ARM 等架构不同,RISC-V 是开放指令集架构(ISA),同一款芯片厂商可以自由裁剪指令扩展。因此"面向 RISC-V 编译"并不像 x86 那样有一个相对统一的指令基线,构建前必须明确告诉编译器目标 ISA 字符串(
rv64gc等)与可选的 CPU 型号(sifive-u74等),这是整个 RISC-V 构建流程的第一原则。
原生编译:通过Make.user指定架构与 CPU
Julia 的构建系统通过根目录下的 Make.inc 定义了大量可配置变量,并约定用户可以将自定义项写入Make.user(该文件不会被 git 跟踪)。在 RISC-V 上编译 Julia,核心是设置以下两个变量:
| 变量 | 作用 | 是否必填 | 取值来源 |
|---|---|---|---|
MARCH | 指定 RISC-V ISA 字符串(即指令集架构),决定允许编译器使用哪些指令扩展 | 必填 | 设备文档或/proc/cpuinfo;可用gcc -march=help查看编译器支持的 flags |
MCPU | 针对具体 CPU 型号进一步优化生成代码 | 可选 | gcc --target-help可查看支持的值列表;不确定时建议留空 |
为什么MARCH在 RISC-V 上是硬性要求
从 Make.inc 的源码可以看到构建系统的默认策略:在未显式设置任何相关变量时,x86 等架构默认使用MARCH=native、MTUNE=native,而 AArch64 默认使用MCPU=native。但对于 RISC-V 则直接触发构建错误:
else ifneq (,$(findstring riscv64,$(ARCH))) # RISC-V doesn't have a native option $(error Building for RISC-V requires a specific MARCH to be set))也就是说:RISC-V 没有native这一选项(因为编译器无法从运行中的处理器可靠探测出完整的 ISA 扩展集合),ARCH检测到riscv64时若未提供MARCH,make会直接报错终止。这正是文档要求"必须手动指定架构"的底层原因。
MARCH/MCPU如何传导到编译过程
Make.inc 中展示了这两个变量对编译器的传导方式:
# Set MARCH-specific flags ifneq ($(MARCH),) CC += -march=$(MARCH) CXX += -march=$(MARCH) FC += -march=$(MARCH) # On RISC-V, don't forward the MARCH ISA string to JULIA_CPU_TARGET, # as it's always incompatible with LLVM's CPU target name parser. ifeq (,$(findstring riscv64,$(ARCH))) JULIA_CPU_TARGET ?= $(MARCH) endif endif # Set MCPU-specific flags ifneq ($(MCPU),) CC += -mcpu=$(MCPU) CXX += -mcpu=$(MCPU) FC += -mcpu=$(MCPU) JULIA_CPU_TARGET ?= $(MCPU) endif可以看到MARCH和MCPU会被拼接到 C、C++ 与 Fortran 编译器(CC/CXX/FC)的调用参数上,从而影响所有 C/Fortran 依赖(如 OpenBLAS、libuv 等)以及 Julia 运行时本身的编译。同时,构建系统默认会把它们同步到 JIT 侧的系统镜像目标JULIA_CPU_TARGET——唯一的例外是 RISC-V 上的MARCH:由于 RISC-V 的 ISA 字符串(如rv64gc)与 LLVM 的 CPU target 名称解析器不兼容,代码中特意跳过了这一步(见 Make.inc)。这正是文档中"可移植构建"示例需要手动补充JULIA_CPU_TARGET的原因。
具体设备示例:StarFive VisionFive2
文档给出的示例是 StarFive VisionFive2 开发板:它搭载的 JH7110 处理器基于 SiFive U74 核心设计。对这类已知 CPU,可以同时设置 ISA 字符串与 CPU 型号以获得针对性优化:
# Make.user MARCH := rv64gc_zba_zbb MCPU := sifive-u74其中:
rv64gc_zba_zbb:rv64gc是 RV64 的通用组合(i整数、m乘除、a原子、f单精度浮点、d双精度浮点、c压缩指令,外加zicsr/zifencei),_zba_zbb追加了位操作(bit-manipulation)扩展,U74 核心支持这些扩展;sifive-u74:告诉编译器按 SiFive U74 的微架构做指令调度与优化。
如何确认你的设备应当使用哪些扩展?两种途径:
- 查阅芯片/开发板官方文档中的 ISA 描述;
- 直接查看运行中系统的
/proc/cpuinfo,其中会列出内核识别的 ISA 扩展。
可移植构建:不锁定特定 CPU
如果你希望产出的 Julia 二进制能够在更广泛的 RISC-V 设备上运行(而不是只为某一款 CPU 特化),可以采用可移植配置:
# Make.user MARCH := rv64gc # also set JULIA_CPU_TARGET to the expanded form of rv64gc # (it normally copies the value of MCPU, which we don't set) JULIA_CPU_TARGET := generic-rv64,i,m,a,f,d,zicsr,zifencei,c这里的关键点是:
MARCH := rv64gc将 ISA 基线收敛到最通用的组合,任何支持 RV64GC 的芯片都能运行;- 由于 RISC-V 上
MARCH不会自动转发为JULIA_CPU_TARGET,且这里也没有设置MCPU(按文档建议留空),因此必须手动把rv64gc的展开形式写入JULIA_CPU_TARGET,告诉 LLVM JIT 生成什么样的指令。generic-rv64,i,m,a,f,d,zicsr,zifencei,c是rv64gc逐项展开后的 LLVM 特性列表,其中generic-rv64是 LLVM 侧的通用 64 位 RISC-V 目标,逗号后依次列出各指令扩展。
补充说明:JULIA_CPU_TARGET的默认值在 Make.inc 中定义为native,因此在 RISC-V 交叉/特化构建场景下,显式设置它是保证系统镜像(sysimage)与运行时 JIT 目标一致的必要手段。关于该变量的更通用讨论,可参见构建分发文档 doc/src/devdocs/build/distributing.md 与 Linux 构建文档 doc/src/devdocs/build/linux.md 中的 "Architecture Customization" 一节(ARM 平台的类似做法可参考 doc/src/devdocs/build/arm.md)。
交叉编译:在高速主机上为 RISC-V 构建
RISC-V 开发板(尤其是初代单板计算机)的计算能力通常有限,在板子上原生编译 Julia 可能耗时极长。官方支持在一台更快的机器上进行交叉编译,整体分三步走。
第一步:准备 RISC-V 交叉编译工具链
需要一套同时支持C、C++ 和 Fortran的 RISC-V 交叉工具链(Julia 的构建依赖大量 C/Fortran 数值库)。官方推荐的方式是获取 riscv-gnu-toolchain 源码仓库并自行构建:
sudo mkdir /opt/riscv && sudo chown $USER /opt/riscv ./configure --prefix=/opt/riscv --with-languages=c,c++,fortran make linux -j$(nproc)要点:
- 安装到
/opt/riscv,后续所有路径均以此为基准; --with-languages=c,c++,fortran必不可少,缺少 Fortran 会导致 OpenBLAS 等依赖无法交叉编译;make linux目标构建的是 Linux 用户态工具链(而非裸机newlib版本),这是运行 Linux 的 RISC-V 开发板所需要的。
第二步:配置 QEMU 用户态模拟与 binfmt
交叉编译出的 RISC-V 二进制无法直接在主机上运行,而 Julia 的构建过程需要在主机上执行部分"目标架构"的程序(例如构建系统会用目标二进制做探测、生成代码等)。解决方案是安装 QEMU 用户态模拟器并启用binfmt支持,使主机内核能够自动把 RISC-V 可执行文件交给 QEMU 执行:
- 具体安装步骤随发行版而异。以 Arch Linux 为例,需要安装
qemu-user-static与qemu-user-static-binfmt两个软件包; binfmt注册表会让内核按 ELF 头自动识别 RISC-V 二进制并调用对应的模拟器;- QEMU 执行 RISC-V 程序时,需要能够找到 RISC-V 的系统根目录(sysroot),这是通过
QEMU_LD_PREFIX环境变量指定的,指向工具链安装路径下的根文件系统(/opt/riscv/sysroot)。
第三步:配置 Julia 的交叉构建变量
在 Make.inc 中,交叉编译通过XC_HOST变量启用:设置后构建系统会自动给所有工具链命令加上$(XC_HOST)-前缀(即CROSS_COMPILE机制),并用它推导目标三元组(triplet)。在之前的Make.user基础上追加:
# Make.user(追加内容) XC_HOST=riscv64-unknown-linux-gnu OS=Linux export QEMU_LD_PREFIX=/opt/riscv/sysrootXC_HOST=riscv64-unknown-linux-gnu:指定交叉工具链前缀,make会调用riscv64-unknown-linux-gnu-gcc、riscv64-unknown-linux-gnu-g++、riscv64-unknown-linux-gnu-gfortran等;OS=Linux:由于是从非 Linux 或未自动识别环境交叉编译,需要显式声明目标操作系统为 Linux(XC_HOST非空时 Makefile 要求显式设置OS,参见 Make.inc 的相关错误处理逻辑);export QEMU_LD_PREFIX:让 QEMU 找到 RISC-V 系统库。
启动构建时,需要确保PATH中包含交叉工具链,例如:
PATH=/opt/riscv/bin:$PATH make -j$(nproc)补充系统库:以 zlib 为例
官方文档特别提醒:我们使用的 RISC-V sysroot 往往非常精简(barren),Julia 构建系统期望的一些系统级库在 sysroot 中可能不存在。文档给出的例子是libz——当前构建依赖于系统提供的 zlib,因此需要把 Julia 构建出的 zlib 拷贝进 sysroot:
make -C deps install-zlib cp -v usr/lib/libz.* /opt/riscv/sysroot/usr/lib cp -v usr/include/z*.h /opt/riscv/sysroot/usr/include这里make -C deps install-zlib调用的是 Julia 依赖子系统的 zlib 构建目标(定义于 deps/zlib.mk,其中install-zlib由staged-install宏生成,另有get-zlib、extract-zlib、configure-zlib、compile-zlib、clean-zlib等配套目标)。在交叉编译阶段,Julia 自身会先把 zlib 交叉编译出来并安装到本地的usr/目录,随后再将其产物复制到/opt/riscv/sysroot下,供后续链接阶段使用。
如果你的交叉构建还遇到其他"找不到系统库"的问题,可以沿用同一思路:先用
make -C deps install-<库名>构建对应依赖,再把库与头文件补齐到 sysroot 中。这是 RISC-V 精简 sysroot 环境下常见的解决路径。
构建流程原理小结
把整条链路串起来看,Julia 在 RISC-V 上的构建涉及三层目标决策:
- ISA 与微架构层:
MARCH(ISA 字符串)与MCPU(CPU 型号)决定 C/C++/Fortran 侧所有代码的生成质量与兼容性,其中MARCH在 RISC-V 上是强制的; - JIT 与系统镜像层:
JULIA_CPU_TARGET决定 LLVM JIT 为 Julia 代码生成什么指令,RISC-V 上需要手动给出rv64gc的展开形式; - 交叉执行层:
XC_HOST驱动工具链前缀切换,QEMU_LD_PREFIX+binfmt解决目标二进制在主机上的运行问题,OS=Linux保证目标平台判定正确。
此外,构建系统对 riscv64 还有一些自动化的默认处理,例如自动设置OPENBLAS_DYNAMIC_ARCH=1与BINARY=64(见 Make.inc),并在生成二进制发行包名时使用riscv64作为架构标识(Make.inc),这些无需用户干预。
常见问题与排障建议
make报错 "Building for RISC-V requires a specific MARCH to be set":说明MARCH未设置。在Make.user中补上MARCH := rv64gc(或更具体的 ISA 字符串)后重试。- 不知道自己的 CPU 支持哪些扩展:执行
cat /proc/cpuinfo查看内核识别的 ISA;用gcc -march=help查看编译器支持的 RISC-V flag 列表;用gcc --target-help查看可用的-mcpu取值。 - 交叉编译时目标二进制无法执行:确认已安装 QEMU 用户态模拟与
binfmt支持,并已export QEMU_LD_PREFIX=/opt/riscv/sysroot。 - 链接时找不到系统库:按上文 zlib 的示例,通过
make -C deps install-<库名>构建依赖后补齐 sysroot。 - 提交问题报告:遇到编译或运行问题时,在官方问题追踪器的
system:riscv标签下查找是否已有相同问题,新建 issue 时请附上cat /proc/cpuinfo输出以及你使用的Make.user内容。
【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/julia
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考