# LLVM Zisk backend This is a summary of work done for building the LLVM backend for Zisk. Assumes knowledge on how Zisk works. For detailed log, see [CraneZisk Sync](/KRXifdQBTLmlQo-iQ_jXeQ) and [Status updates](/cr4cdKSKT4OZ_rKAPzK-wg) The code with Zisk-related experiments is available at: - https://github.com/aborg-dev/llvm-project/commit/315c4c94b2875b49fdbc60c8a462fbb7aea73525 - https://github.com/0xPolygonHermez/rust/tree/aborg/zisk_llvm ## High-level overview and goals Zisk already uses LLVM through Rustc. The target `riscv64ima-polygon-ziskos-elf` corresponds to [LLVM RISCV backend](https://github.com/llvm/llvm-project/tree/main/llvm/lib/Target/RISCV). The goal of the project is to generate Zisk bytecode directly from LLVM to bypass a few limitations of RISC-V: - Move from limited 32 registers to *infinite* number of registers - Allow direct operations with immediates up to 64 bits compared to only 12 bits in RISC-V ## High-level design and execution plan LLVM backends are quite large and complex: - RISC-V: 80k lines of code (45k C++, 30k TableGen) - WebAssembly: 20k lines of code (17k C++, 3k TableGen) So writing one from scratch is a lot of work. Instead we decided to fork RISC-V backend that can already be used to generate Zisk and iterate from there: - Fork LLVM RISC-V backend (in reality just start modifying it in-place) - Build Rust compiler with modified LLVM and use it as Zisk toolchain - Modify the backend to emit bytecode that leverages Zisk for performance gains (but deviating from RISC-V ISA) ## Lessons learned Onboarding to LLVM is a massive time commitment, at least 2 months. Moving away from established conventions is very hard, a lot of interdependent code. For any non-trivial idea, consulting with an active LLVM developer is highly recommended. Best starting points seem to be existing "simpler" backends like RISC-V and WebAssembly. ## Ideas ### RISC-V Zisk backend is based on RISC-V. The code for the backend is located at https://github.com/aborg-dev/llvm-project/tree/rust-llvm/rustc/19.1-2024-12-03/llvm/lib/Target/RISCV. A good overview of the backend is given in https://www.youtube.com/watch?v=AFaIP-dF-RA. ### WebAssembly WebAssembly is a stack machine with a lot of similarities to Zisk LLVM can target WebAssembly as a backend: - https://llvm.org/devmtg/2015-10/slides/BastienGohman-WebAssembly-HereBeDragons.pdf - https://github.com/aborg-dev/llvm-project/tree/rust-llvm/rustc/19.1-2024-12-03/llvm/lib/Target/WebAssembly In particular, the ideas relevant for Zisk are - Definition of registers: https://github.com/aborg-dev/llvm-project/blob/rust-llvm/rustc/19.1-2024-12-03/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td - Translation from virtual registers to WebAssembly stack slots: https://github.com/aborg-dev/llvm-project/blob/rust-llvm/rustc/19.1-2024-12-03/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp ## Development Below is the set of technical recipies for building and using Zisk LLVM backend. Codebase is available at: - https://github.com/0xPolygonHermez/rust/tree/aborg/zisk_llvm - https://github.com/aborg-dev/llvm-project - For fresh start, use upstream https://github.com/rust-lang/llvm-project ### Setting up the project The first step to set up the development environment is to: - Fork Rust compiler https://github.com/0xPolygonHermez/rust - Fork Rust LLVM fork https://github.com/rust-lang/llvm-project/tree/7e8c93c87c611f21d9bd95100563392f4c18bfe7 Relevant article: https://sourcecodeartisan.com/2020/09/13/llvm-backend-1.html ### Building Rust compiler with new LLVM Rust needs to be built with the config that enables local LLVM build: config.toml: ```toml [llvm] download-ci-llvm = false optimize = false static-libstdcpp = true link-shared = true [build] configure-args = [] [install] prefix = "rust/build/local" sysconfdir = "rust/build/etc" ``` Then run: ```sh DESTDIR=build ./x.py build && ./x.py install ``` Note, that this step will take from 30 minutes to 2 hours, depending on how powerful your machine is. You can speed it up in the future by only rebuilding the LLVM library: ```sh cd rust/build/x86_64-unknown-linux-gnu/llvm/build DESTDIR="" cmake --build . --target install --config Release -- -j 32 cp ../lib/libLLVM.so.19.1-rust-1.83.0-nightly .../rust/build/host/stage2/lib ``` ### Register new Rust toolchain ```sh rustup toolchain link custom_zisk .../rust/build/host/stage2 ``` ### Build and run Zisk program using a new toolchain Uses Zisk hello_world project: ```sh cargo clean && cargo +custom_zisk build -Zbuild-std=panic_abort,std -Zbuild-std-features=compiler-builtins-mem --target riscv64ima-polygon-ziskos-elf --release cargo run --bin ziskemu -- -i ../hello_world/build/input.bin -xm -e ../hello_world/target/riscv64ima-polygon-ziskos-elf/release/sha_hasher ``` For debug mode, use: ```sh LLVM_DEBUG=1 RUST_BACKTRACE=full cargo clean && cargo +custom_zisk build -Zbuild-std=panic_abort,std -Zbuild-std-features=compiler-builtins-mem --target riscv64ima-polygon-ziskos-elf --release ```