Skip to content

Commit

Permalink
Initial RISC-V support.
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Fan <alex.fan.q@gmail.com>
  • Loading branch information
maleadt and alexfanqi committed Oct 14, 2024
1 parent 35bf824 commit 25092a3
Show file tree
Hide file tree
Showing 26 changed files with 599 additions and 18 deletions.
13 changes: 12 additions & 1 deletion Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -938,8 +938,12 @@ endif

#If nothing is set default to native unless we are cross-compiling
ifeq ($(MARCH)$(MCPU)$(MTUNE)$(JULIA_CPU_TARGET)$(XC_HOST),)
ifeq ($(ARCH),aarch64) #ARM recommends only setting MCPU for AArch64
ifeq ($(ARCH),aarch64)
# ARM recommends only setting MCPU for AArch64
MCPU=native
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))
else
MARCH=native
MTUNE=native
Expand Down Expand Up @@ -995,6 +999,9 @@ endif
ifneq (,$(findstring arm,$(ARCH)))
DIST_ARCH:=arm
endif
ifneq (,$(findstring riscv64,$(ARCH)))
DIST_ARCH:=riscv64
endif

JULIA_BINARYDIST_FILENAME := julia-$(JULIA_COMMIT)-$(DIST_OS)$(DIST_ARCH)
endif
Expand All @@ -1018,8 +1025,12 @@ 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),)
Expand Down
5 changes: 4 additions & 1 deletion base/binaryplatforms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ const arch_mapping = Dict(
"armv7l" => "arm(v7l)?", # if we just see `arm-linux-gnueabihf`, we assume it's `armv7l`
"armv6l" => "armv6l",
"powerpc64le" => "p(ower)?pc64le",
"riscv64" => "riscv64",
"riscv64" => "(rv64|riscv64)",
)
# Keep this in sync with `CPUID.ISAs_by_family`
# These are the CPUID side of the microarchitectures targeted by GCC flags in BinaryBuilder.jl
Expand Down Expand Up @@ -631,6 +631,9 @@ const arch_march_isa_mapping = let
"a64fx" => get_set("aarch64", "a64fx"),
"apple_m1" => get_set("aarch64", "apple_m1"),
],
"riscv64" => [
"riscv64" => get_set("riscv64", "riscv64")
],
"powerpc64le" => [
"power8" => get_set("powerpc64le", "power8"),
],
Expand Down
3 changes: 3 additions & 0 deletions base/cpuid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const ISAs_by_family = Dict(
"a64fx" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_sha2, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fullfp16, JL_AArch64_sve))),
"apple_m1" => ISA(Set((JL_AArch64_v8_5a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_sha3, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fp16fml, JL_AArch64_fullfp16, JL_AArch64_dotprod, JL_AArch64_rcpc, JL_AArch64_altnzcv))),
],
"riscv64" => [
"riscv64" => ISA(Set{UInt32}()),
],
"powerpc64le" => [
# We have no way to test powerpc64le features yet, so we're only going to declare the lowest ISA:
"power8" => ISA(Set{UInt32}()),
Expand Down
20 changes: 20 additions & 0 deletions cli/trampolines/trampolines_riscv64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#include "common.h"
#include "../../src/jl_exported_funcs.inc"

#define SEP ;

#define XX(name) \
.global CNAME(name) SEP \
.cfi_startproc SEP \
.p2align 2 SEP \
CNAME(name)##: SEP \
auipc t3, %pcrel_hi(CNAMEADDR(name)) SEP \
ld t3, %pcrel_lo(CNAME(name))(t3) SEP \
jr t3 SEP \
.cfi_endproc SEP \

JL_RUNTIME_EXPORTED_FUNCS(XX)
JL_CODEGEN_EXPORTED_FUNCS(XX)
#undef XX
9 changes: 6 additions & 3 deletions contrib/generate_precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,15 @@ if Artifacts !== nothing
using Artifacts, Base.BinaryPlatforms, Libdl
artifacts_toml = abspath(joinpath(Sys.STDLIB, "Artifacts", "test", "Artifacts.toml"))
artifact_hash("HelloWorldC", artifacts_toml)
oldpwd = pwd(); cd(dirname(artifacts_toml))
macroexpand(Main, :(@artifact_str("HelloWorldC")))
cd(oldpwd)
artifacts = Artifacts.load_artifacts_toml(artifacts_toml)
platforms = [Artifacts.unpack_platform(e, "HelloWorldC", artifacts_toml) for e in artifacts["HelloWorldC"]]
best_platform = select_platform(Dict(p => triplet(p) for p in platforms))
if best_platform !== nothing
# @artifact errors for unsupported platforms
oldpwd = pwd(); cd(dirname(artifacts_toml))
macroexpand(Main, :(@artifact_str("HelloWorldC")))
cd(oldpwd)
end
dlopen("libjulia$(Base.isdebugbuild() ? "-debug" : "")", RTLD_LAZY | RTLD_DEEPBIND)
"""
end
Expand Down
1 change: 1 addition & 0 deletions contrib/normalize_triplet.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'i686': "i\\d86",
'aarch64': "(arm|aarch)64",
'armv7l': "arm(v7l)?",
'riscv64': "(rv64|riscv64)",
'powerpc64le': "p(ower)?pc64le",
}
platform_mapping = {
Expand Down
1 change: 1 addition & 0 deletions doc/src/devdocs/build/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Notes for various operating systems:
Notes for various architectures:

* [ARM](https://github.com/JuliaLang/julia/blob/master/doc/src/devdocs/build/arm.md)
* [RISC-V](https://github.com/JuliaLang/julia/blob/master/doc/src/devdocs/build/riscv.md)

## Required Build Tools and External Libraries

Expand Down
103 changes: 103 additions & 0 deletions doc/src/devdocs/build/riscv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# RISC-V (Linux)

Julia has experimental support for 64-bit RISC-V (RV64) processors running
Linux. This file provides general guidelines for compilation, in addition to
instructions for specific devices.

A list of [known issues](https://github.com/JuliaLang/julia/labels/system:riscv)
for RISC-V is available. If you encounter difficulties, please create an issue
including the output from `cat /proc/cpuinfo`.


## Compiling Julia

For now, Julia will need to be compiled entirely from source, i.e., including
all of its dependencies. This can be accomplished with the following
`Make.user`:

```make
USE_BINARYBUILDER := 0
```

Additionally, it is required to indicate what architecture, and optionally which
CPU to build for. This can be done by setting the `MARCH` and `MCPU` variables
in `Make.user`

The `MARCH` variable needs to be set to a RISC-V ISA string, which can be found by
looking at the documentation of your device, or by inspecting `/proc/cpuinfo`. Only
use flags that your compiler supports, e.g., run `gcc -march=help` to see a list of
supported flags. A common value is `rv64gc`, which is a good starting point.

The `MCPU` variable is optional, and can be used to further optimize the
generated code for a specific CPU. If you are unsure, it is recommended to leave
it unset. You can find a list of supported values by running `gcc --target-help`.

For example, if you are using a StarFive VisionFive2, which contains a JH7110
processor based on the SiFive U74, you can set these flags as follows:

```make
MARCH := rv64gc_zba_zbb
MCPU := sifive-u74
```

If you prefer a portable build, you could use:

```make
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
```

### Cross-compilation

A native build on a RISC-V device may take a very long time, so it's also
possible to cross-compile Julia on a faster machine.

First, get a hold of a RISC-V cross-compilation toolchain that provides
support for C, C++ and Fortran. This can be done by checking-out the
[riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain)
repository and building it as follows:

```sh
sudo mkdir /opt/riscv && sudo chown $USER /opt/riscv
./configure --prefix=/opt/riscv --with-languages=c,c++,fortran
make linux -j$(nproc)
```

Then, install the QEMU user-mode emulator for RISC-V, along with `binfmt`
support to enable execution of RISC-V binaries on the host machine. The
exact steps depend on your distribution, e.g., on Arch Linux it involves
installing the `qemu-user-static` and `qemu-user-static-binfmt` packages.
Note that to actually execute RISC-V binaries, QEMU will need to be able to
find the RISC-V system root, which can be achieved by setting the
`QEMU_LD_PREFIX` environment variable to the path of the root filesystem.

Finally, compile Julia with the following `Make.user` variables (in addition to
the ones from the previous section):

```make
XC_HOST=riscv64-unknown-linux-gnu
OS=Linux
export QEMU_LD_PREFIX=/opt/riscv/sysroot
```

Note that you will have to execute `make` with `PATH` set to include the
cross-compilation toolchain, e.g., by running:

```sh
PATH=/opt/riscv/bin:$PATH make -j$(nproc)
```

Because of the RISC-V sysroot we use being very barren, you may need to
add additional libraries that the Julia build system currently expects
to be available system-wide. For example, the build currently relies on
a system-provided `libz`, so you may need to copy this library from the
Julia build into the system root:

```sh
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
```
Loading

0 comments on commit 25092a3

Please sign in to comment.