Skip to content

Commit 6e48612

Browse files
committed
Merge pull request #1913 from pguyot/w42/jit-riscv32
Just in time compilation for riscv32 architecture Continuation of: - #1881 - #1829 - #1891 - #1838 - #1900 - #1901 - #1903 - #1981 These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents f509d84 + 334c7f8 commit 6e48612

31 files changed

+9708
-88
lines changed

.github/workflows/build-and-test.yaml

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,33 @@ jobs:
366366
arch: "s390x"
367367
library-arch: s390x-linux-gnu
368368

369+
# riscv32-ilp32 build
370+
- os: "ubuntu-24.04"
371+
cc: "riscv32-unknown-linux-gnu-gcc"
372+
cxx: "riscv32-unknown-linux-gnu-g++"
373+
cflags: "-O2"
374+
otp: "28"
375+
elixir_version: "1.17"
376+
rebar3_version: "3.24.0"
377+
cmake_opts_other: "-DAVM_WARNINGS_ARE_ERRORS=ON -DCMAKE_TOOLCHAIN_FILE=${RUNNER_TEMP}/riscv32_ilp32_toolchain.cmake"
378+
compiler_pkgs: "qemu-user qemu-user-binfmt binfmt-support"
379+
arch: "riscv32"
380+
library-arch: riscv32-linux-gnu-ilp32
381+
382+
# riscv32-ilp32 build + jit
383+
- os: "ubuntu-24.04"
384+
cc: "riscv32-unknown-linux-gnu-gcc"
385+
cxx: "riscv32-unknown-linux-gnu-g++"
386+
cflags: "-O2"
387+
otp: "28"
388+
elixir_version: "1.17"
389+
rebar3_version: "3.24.0"
390+
cmake_opts_other: "-DAVM_DISABLE_JIT=OFF -DAVM_JIT_TARGET_ARCH=riscv32 -DCMAKE_TOOLCHAIN_FILE=${RUNNER_TEMP}/riscv32_ilp32_toolchain.cmake"
391+
compiler_pkgs: "qemu-user qemu-user-binfmt binfmt-support"
392+
arch: "riscv32"
393+
library-arch: riscv32-linux-gnu-ilp32
394+
jit_target_arch: "riscv32"
395+
369396
env:
370397
ImageOS: ${{ matrix.container == 'ubuntu:20.04' && 'ubuntu20' || matrix.os == 'ubuntu-20.04' && 'ubuntu20' || matrix.os == 'ubuntu-22.04' && 'ubuntu22' || matrix.os == 'ubuntu-24.04' && 'ubuntu24' || 'ubuntu24' }}
371398
CC: ${{ matrix.cc }}
@@ -386,7 +413,7 @@ jobs:
386413
run: sudo dpkg --add-architecture i386
387414

388415
- name: "Setup cross compilation architecture"
389-
if: matrix.library-arch != ''
416+
if: matrix.library-arch != '' && matrix.library-arch != 'riscv32-linux-gnu-ilp32'
390417
run: |
391418
# Replace Azure mirrors with official Ubuntu repositories
392419
sudo sed -i 's|azure\.||g' /etc/apt/sources.list
@@ -420,6 +447,97 @@ jobs:
420447
set(MBEDTLS_LIBRARIES_DIR /usr/lib/${{ matrix.library-arch }})
421448
EOF
422449
450+
- name: "Setup cross compilation architecture (riscv32)"
451+
if: matrix.library-arch == 'riscv32-linux-gnu-ilp32'
452+
run: |
453+
sudo dpkg --add-architecture ${{ matrix.arch }}
454+
455+
# Download toolchain and libraries from release
456+
gh release download riscv-toolchain-2025.10.18 \
457+
-R pguyot/crossbuild-essential-riscv32 \
458+
--pattern 'riscv32-gnu-toolchain-ilp32_2025.10.18_amd64.deb' \
459+
--pattern 'libc6-ilp32_2.39-0ubuntu1_riscv32.deb' \
460+
--pattern 'libc6-dev-ilp32_2.39-0ubuntu1_riscv32.deb' \
461+
--pattern 'libc6-dbg-ilp32_2.39-0ubuntu1_riscv32.deb' \
462+
--pattern 'zlib1g-ilp32_1.3.1-0ubuntu1_riscv32.deb' \
463+
--pattern 'zlib1g-dev-ilp32_1.3.1-0ubuntu1_riscv32.deb' \
464+
--pattern 'libmbedcrypto7-ilp32_2.28.8-0ubuntu1_riscv32.deb' \
465+
--pattern 'libmbedtls-dev-ilp32_2.28.8-0ubuntu1_riscv32.deb' \
466+
--pattern 'libmbedtls14-ilp32_2.28.8-0ubuntu1_riscv32.deb' \
467+
--pattern 'libmbedx509-1-ilp32_2.28.8-0ubuntu1_riscv32.deb'
468+
469+
# Install the toolchain
470+
sudo dpkg -i riscv32-gnu-toolchain-ilp32_2025.10.18_amd64.deb
471+
472+
# Add to PATH for all subsequent steps
473+
echo "/opt/riscv32-ilp32/bin" >> $GITHUB_PATH
474+
475+
# Install the libs
476+
sudo dpkg -i libc6-ilp32_2.39-0ubuntu1_riscv32.deb
477+
sudo dpkg -i libc6-dev-ilp32_2.39-0ubuntu1_riscv32.deb
478+
sudo dpkg -i libc6-dbg-ilp32_2.39-0ubuntu1_riscv32.deb
479+
480+
sudo dpkg -i zlib1g-ilp32_1.3.1-0ubuntu1_riscv32.deb
481+
sudo dpkg -i zlib1g-dev-ilp32_1.3.1-0ubuntu1_riscv32.deb
482+
483+
# Install mbedtls runtime packages first (in dependency order)
484+
sudo dpkg -i libmbedcrypto7-ilp32_2.28.8-0ubuntu1_riscv32.deb
485+
sudo dpkg -i libmbedx509-1-ilp32_2.28.8-0ubuntu1_riscv32.deb
486+
sudo dpkg -i libmbedtls14-ilp32_2.28.8-0ubuntu1_riscv32.deb
487+
# Then install the dev package
488+
sudo dpkg -i libmbedtls-dev-ilp32_2.28.8-0ubuntu1_riscv32.deb
489+
490+
sudo sed -i '/Types: deb/a Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources
491+
492+
cat > ${RUNNER_TEMP}/riscv32_ilp32_toolchain.cmake <<'EOF'
493+
# Toolchain file for RISC-V32 ILP32 (RV32-IMAC) cross-compilation
494+
set(CMAKE_SYSTEM_NAME Linux)
495+
set(CMAKE_SYSTEM_PROCESSOR riscv32)
496+
set(CMAKE_C_LIBRARY_ARCHITECTURE riscv32-linux-gnu-ilp32)
497+
498+
# Specify the cross compiler
499+
set(CMAKE_C_COMPILER riscv32-unknown-linux-gnu-gcc)
500+
set(CMAKE_CXX_COMPILER riscv32-unknown-linux-gnu-g++)
501+
502+
# Specify the target architecture
503+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=rv32imac -mabi=ilp32" CACHE STRING "" FORCE)
504+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=rv32imac -mabi=ilp32" CACHE STRING "" FORCE)
505+
506+
# Set up paths for cross-compiled libraries
507+
set(ZLIB_LIBRARY /usr/lib/riscv32-linux-gnu-ilp32/libz.so CACHE FILEPATH "")
508+
set(ZLIB_INCLUDE_DIR /usr/include/riscv32-linux-gnu CACHE PATH "")
509+
set(ZLIB_FOUND TRUE CACHE BOOL "")
510+
511+
# MbedTLS configuration
512+
set(MBEDTLS_ROOT_DIR /usr)
513+
set(MBEDTLS_LIBRARIES_DIR /usr/lib/riscv32-linux-gnu-ilp32)
514+
515+
# Add cross-compilation include path to compiler flags
516+
include_directories(SYSTEM /usr/include/riscv32-linux-gnu)
517+
518+
# Search for programs in the build host directories
519+
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
520+
521+
# Search for libraries and headers in the target directories
522+
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
523+
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
524+
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
525+
EOF
526+
527+
# Set up qemu-user binfmt to find libraries
528+
sudo ln -s /opt/riscv32-ilp32/sysroot/lib/ld-linux-riscv32-ilp32.so.1 /lib/ld-linux-riscv32-ilp32.so.1
529+
sudo mkdir -p /usr/gnemul
530+
sudo ln -s /opt/riscv32-ilp32/sysroot /usr/gnemul/qemu-riscv32
531+
532+
# Copy cross-compiled libraries to sysroot for qemu-user
533+
sudo cp /usr/lib/${{ matrix.library-arch }}/libz.so.1* /opt/riscv32-ilp32/sysroot/lib/
534+
sudo cp /usr/lib/${{ matrix.library-arch }}/libmbedtls.so.14 /opt/riscv32-ilp32/sysroot/lib/
535+
sudo cp /usr/lib/${{ matrix.library-arch }}/libmbedcrypto.so.7 /opt/riscv32-ilp32/sysroot/lib/
536+
sudo cp /usr/lib/${{ matrix.library-arch }}/libmbedx509.so.1 /opt/riscv32-ilp32/sysroot/lib/
537+
538+
env:
539+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
540+
423541
- name: "APT update"
424542
run: sudo apt update -y
425543

@@ -630,7 +748,7 @@ jobs:
630748
valgrind --error-exitcode=1 ./src/AtomVM tests/libs/jit/test_jit.avm
631749
632750
- name: "Test: test_jit.avm"
633-
timeout-minutes: 20
751+
timeout-minutes: 60
634752
if: matrix.otp != '21' && matrix.otp != '22'
635753
working-directory: build
636754
run: |

.github/workflows/pico-build.yaml

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,56 @@ jobs:
4141
strategy:
4242
matrix:
4343
board: ["pico", "pico_w", "pico2"]
44-
language: ["cpp"]
44+
platform: [""]
45+
jit: ["", "-DAVM_DISABLE_JIT=OFF"]
46+
include:
47+
- board: "pico2"
48+
platform: "-DPICO_PLATFORM=rp2350-riscv"
49+
jit: ""
50+
51+
- board: "pico2"
52+
platform: "-DPICO_PLATFORM=rp2350-riscv"
53+
jit: "-DAVM_DISABLE_JIT=OFF"
4554

4655
steps:
4756
- name: Checkout repo
4857
uses: actions/checkout@v4
4958

59+
- uses: erlef/setup-beam@v1
60+
with:
61+
otp-version: "28.1"
62+
rebar3-version: "3.24.0"
63+
hexpm-mirrors: |
64+
https://builds.hex.pm
65+
https://repo.hex.pm
66+
https://cdn.jsdelivr.net/hex
67+
5068
- name: "apt update"
5169
run: sudo apt update
5270

5371
- name: "Install deps"
5472
run: |
5573
sudo apt install -y \
5674
cmake doxygen gperf ninja-build gcc-arm-none-eabi \
57-
libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib \
58-
erlang-base erlang-dev erlang-dialyzer erlang-eunit rebar3
75+
libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
76+
77+
- name: Install riscv32 toolchain
78+
if: matrix.platform == '-DPICO_PLATFORM=rp2350-riscv'
79+
run: |
80+
sudo mkdir -p /opt/riscv32-toolchain
81+
cd /opt/riscv32-toolchain
82+
sudo wget -q https://github.com/raspberrypi/pico-sdk-tools/releases/download/v2.2.0-3/riscv-toolchain-15-x86_64-lin.tar.gz
83+
sudo tar xzf riscv-toolchain-15-x86_64-lin.tar.gz
84+
sudo rm riscv-toolchain-15-x86_64-lin.tar.gz
85+
echo "/opt/riscv32-toolchain/bin" >> $GITHUB_PATH
5986
6087
- name: "Git config safe.directory for codeql"
6188
run: git config --global --add safe.directory /__w/AtomVM/AtomVM
6289

6390
- name: "Initialize CodeQL"
6491
uses: github/codeql-action/init@v3
6592
with:
66-
languages: ${{matrix.language}}
93+
languages: "cpp"
6794
build-mode: manual
6895
queries: +./code-queries/term-to-non-term-func.ql,./code-queries/non-term-to-term-func.ql
6996

@@ -74,7 +101,7 @@ jobs:
74101
set -euo pipefail
75102
mkdir build
76103
cd build
77-
cmake .. -G Ninja -DPICO_BOARD=${{ matrix.board }}
104+
cmake .. -G Ninja -DPICO_BOARD=${{ matrix.board }} ${{ matrix.platform }} ${{ matrix.jit }}
78105
ninja
79106
80107
- name: "Perform CodeQL Analysis"
@@ -97,7 +124,7 @@ jobs:
97124
mkdir build.nosmp
98125
cd build.nosmp
99126
# TODO: fix all warnings and enable -DAVM_WARNINGS_ARE_ERRORS=ON
100-
cmake .. -G Ninja -DPICO_BOARD=${{ matrix.board }} -DAVM_DISABLE_SMP=1
127+
cmake .. -G Ninja -DPICO_BOARD=${{ matrix.board }} ${{ matrix.jit }} -DAVM_DISABLE_SMP=1
101128
cmake --build . --target=rp2_tests
102129
103130
- name: Run tests with rp2040js
@@ -112,7 +139,7 @@ jobs:
112139
npx tsx run-tests.ts ../build.nosmp/tests/rp2_tests.uf2 ../build.nosmp/tests/test_erl_sources/rp2_test_modules.uf2
113140
114141
- name: Build atomvmlib.uf2
115-
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w'
142+
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w' && matrix.platform == '' && matrix.jit == ''
116143
shell: bash
117144
run: |
118145
set -euo pipefail
@@ -122,7 +149,7 @@ jobs:
122149
make atomvmlib-${{ matrix.board }}.uf2
123150
124151
- name: Rename AtomVM and write sha256sum
125-
if: startsWith(github.ref, 'refs/tags/')
152+
if: startsWith(github.ref, 'refs/tags/') && matrix.platform == '' && matrix.jit == ''
126153
shell: bash
127154
run: |
128155
pushd src/platforms/rp2/build
@@ -137,7 +164,7 @@ jobs:
137164
popd
138165
139166
- name: Rename atomvmlib and write sha256sum
140-
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w'
167+
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w' && matrix.platform == '' && matrix.jit == ''
141168
shell: bash
142169
run: |
143170
pushd build/libs
@@ -148,7 +175,7 @@ jobs:
148175
149176
- name: Release (Pico & Pico2)
150177
uses: softprops/action-gh-release@v1
151-
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w'
178+
if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w' && matrix.platform == '' && matrix.jit == ''
152179
with:
153180
draft: true
154181
fail_on_unmatched_files: true
@@ -160,7 +187,7 @@ jobs:
160187
161188
- name: Release (PicoW)
162189
uses: softprops/action-gh-release@v1
163-
if: startsWith(github.ref, 'refs/tags/') && matrix.board == 'pico_w'
190+
if: startsWith(github.ref, 'refs/tags/') && matrix.board == 'pico_w' && matrix.platform == '' && matrix.jit == ''
164191
with:
165192
draft: true
166193
fail_on_unmatched_files: true

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ if (NOT AVM_DISABLE_JIT AND NOT DEFINED AVM_JIT_TARGET_ARCH)
6464
endif()
6565
endif()
6666

67-
set(AVM_PRECOMPILED_TARGETS "x86_64;aarch64;armv6m;armv6m+float32" CACHE STRING "Targets to precompile code to if AVM_DISABLE_JIT is OFF or AVM_ENABLE_PRECOMPILED is ON")
67+
set(AVM_PRECOMPILED_TARGETS "x86_64;aarch64;armv6m;armv6m+float32;riscv32" CACHE STRING "Targets to precompile code to if AVM_DISABLE_JIT is OFF or AVM_ENABLE_PRECOMPILED is ON")
6868

6969
if((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") OR
7070
(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") OR

doc/src/atomvm-internals.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Following BEAM, there are two flavors of the emulator: jit and emu, but eventual
137137
- Native: the VM only runs native code and all code must be precompiled on the desktop using the JIT compiler (which effectively is a AOT or Ahead-of-Time compiler). In this mode, it is not necessary to bundle the jit compiler on the embedded target.
138138
- Hybrid: the VM can run native code as well as emulated BEAM code and some code is precompiled on the desktop.
139139

140-
JIT is available on some platforms (currently only x86_64, aarch64 and armv6m) and compiles Erlang bytecode at runtime. Erlang bytecode is never interpreted. EMU is available on all platforms and Erlang bytecode is interpreted.
140+
JIT is available on some platforms (currently x86_64, aarch64, armv6m and riscv32) and compiles Erlang bytecode at runtime. Erlang bytecode is never interpreted. EMU is available on all platforms and Erlang bytecode is interpreted.
141141

142142
Modules can include precompiled code in a dedicated beam chunk with name 'avmN'. The chunk can contain native code for several architectures, however it may only contain native code for a given version of the native interface. Current version is 1. This native code is executed by the jit-flavor of the emulator as well as the emu flavor if execution of precompiled is enabled.
143143

@@ -154,9 +154,16 @@ The JIT compiler is written in Erlang and is therefore precompiled. When a proce
154154

155155
JIT compiler is composed of two main interfaces : backend and stream.
156156

157-
A backend implementation is required for each architecture. The backend is called by jit module as it translates bytecodes to machine code. The current implementations are `jit_x86_64` and `jit_aarch64` which are suitable for systems with System V X86 64 ABI or AArch64 ABI.
157+
A backend implementation is required for each architecture. The backend is called by jit module as it translates bytecodes to machine code. The current implementations are :
158+
- `jit_x86_64` for System V X86 64 ABI
159+
- `jit_aarch64` for AArch64 ABI
160+
- `jit_armv6m` for AArch32 ABI
161+
- `jit_riscv32` for rv32imc ilp32 ABI.
158162

159-
A stream implementation is responsible for streaming the machine code, especially in the context of low memory. Two implementations currently exist: `jit_stream_binary` that streams assembly code to an Erlang binary, suitable for tests and precompilation on the desktop, and `jit_stream_mmap` that streams assembly code in an `mmap(2)` allocated page, suitable for JIT compilation on Unix.
163+
A stream implementation is responsible for streaming the machine code, especially in the context of low memory. Three implementations currently exist:
164+
- `jit_stream_binary` that streams assembly code to an Erlang binary, suitable for tests and precompilation on the desktop
165+
- `jit_stream_mmap` that streams assembly code in an `mmap(2)` allocated page, suitable for JIT compilation on Unix
166+
- `jit_stream_flash` available on Pico that allows for embedded JIT.
160167

161168
### Embedded JIT and Native
162169

libs/jit/include/jit.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
-define(JIT_ARCH_X86_64, 1).
2828
-define(JIT_ARCH_AARCH64, 2).
2929
-define(JIT_ARCH_ARMV6M, 3).
30+
-define(JIT_ARCH_RISCV32, 4).
3031

3132
-define(JIT_VARIANT_PIC, 1).
3233
-define(JIT_VARIANT_FLOAT32, 2).

libs/jit/src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ set(ERLANG_MODULES
3232
jit_aarch64_asm
3333
jit_armv6m
3434
jit_armv6m_asm
35+
jit_riscv32
36+
jit_riscv32_asm
3537
jit_x86_64
3638
jit_x86_64_asm
3739
)

libs/jit/src/jit_precompile.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ compile(Target, Dir, Path) ->
9393
"x86_64" -> ?JIT_ARCH_X86_64;
9494
"aarch64" -> ?JIT_ARCH_AARCH64;
9595
"armv6m" -> ?JIT_ARCH_ARMV6M;
96+
"riscv32" -> ?JIT_ARCH_RISCV32;
9697
_ -> error({unsupported_target, Target})
9798
end,
9899

0 commit comments

Comments
 (0)