Rust on Dreamcast: Difference between revisions

From dreamcast.wiki
Jump to navigation Jump to search
Line 101: Line 101:
** Add sh-dreamcast as an official tier 3 Rust target
** Add sh-dreamcast as an official tier 3 Rust target
* Expand <code>cargo-dc</code> functionality, including integrating Rust-based disc tools to generate disc images
* Expand <code>cargo-dc</code> functionality, including integrating Rust-based disc tools to generate disc images
=gccrs=
'''gccrs''' implements a new Rust compiler frontend for GCC. This essentially means creating a separate new Rust compiler from the ground up using the GCC toolchain infrastructure. This project is in early stages and is targeting the Rust 1.49 revision from December 2020. As of this writing (February 2024), it is not yet able to compile Rust's <code>libcore</code>, so many basic language features are unimplemented or not functional. Additionally, Rust standard tooling like <code>cargo</code> is not available. Borrow checking is not implemented, but the project plans to later use the next-generation Rust borrow checker [https://github.com/rust-lang/polonius Polonius] from the official Rust project.
It is possible to use this compiler by building the GCC 14.0.1-dev toolchain or the '''gccrs''' latest toolchain. GCC 14.0.1-dev will get you the latest code upstreamed by the '''gccrs''' team into the main development branch of GCC, while the '''gccrs''' git repo will get you the absolute latest bleeding edge updates to '''gccrs'''. The GCC 14.0.1-dev configuration file is available within the official KallistiOS repo's <code>dc-chain</code> script, while the latest '''gccrs''' configuration is available within the [https://github.com/darcagn/rust-for-dreamcast Rust for Dreamcast] repository. Brief instructions follow for setting up the latest '''gccrs''' toolchain. See [[Getting Started with Dreamcast development]] for more detailed information on how to set up and run <code>dc-chain</code>.
==Building a gccrs-enabled toolchain==
Follow the [[Getting Started with Dreamcast development]] guide for creating a Dreamcast toolchain until you arrive at the instructions for setting up the <code>dc-chain</code> configuration file. At this point, you should have a shell open to <code>/opt/toolchains/dc/kos/utils/dc-chain</code>.
Clone the [https://github.com/darcagn/rust-for-dreamcast Rust for Dreamcast] repository:
git clone https://github.com/darcagn/rust-for-dreamcast.git rust
Copy the GCC patch in place:
cp rust/toolchain/gcc-rs-kos.diff patches/
Copy the <code>dc-chain</code> configuration file into place:
cp rust/toolchain/config.mk.gccrs.sample config.mk
Make any desired changes to the configuration (e.g., change <code>makejobs=-j2</code> to the number of CPU threads you'd like to use during compilation). Note that to avoid conflicting with an existing stable toolchain at the default path (i.e. <code>/opt/toolchains/dc/sh-elf</code>), we will be installing to <code>/opt/toolchains/dc/gccrs/sh-elf</code> instead. To begin compilation and installation, run:
make build-sh4
After building everything, you can clean up the extraneous files in your <code>dc-chain</code> directory by entering:
make clean
==Setting up Makefiles to compile Rust modules==
As mentioned before, <code>cargo</code> is not available to use with '''gccrs''', so for our example, we will place our <code>.rs</code> modules within a typical KallistiOS <code>Makefile</code> project. If we assume the module file is named <code>example.rs</code>, you'll need to add <code>example.rox</code> as an object file in your <code>Makefile</code>'s <code>OBJS =</code> declaration. Additionally, you'll need to add the following lines so that <code>make</code> knows how to compile Rust modules into <code>rox</code> object files:
<syntaxhighlight lang="make">
%.rox: %.rs
kos-cc -frust-incomplete-and-experimental-compiler-do-not-use $(CFLAGS) -c $< -o $@
</syntaxhighlight>
Alternatively, you can add those lines to your KallistiOS <code>Makefile.rules</code> file to avoid having to place it in every project's <code>Makefile</code>.
In your <code>example.rs</code> file, your <code>main</code> function will need to be declared like so:.
<syntaxhighlight lang="rust">
#[no_mangle]
pub extern fn main() -> i32 {
    [...]
}
</syntaxhighlight>
Make sure before you compile your code that you set <code>export KOS_CC_BASE="/opt/toolchains/dc/gccrs/sh-elf"</code> in your KallistiOS <code>environ.sh</code> file or <code>make</code> will not find your '''gccrs''' compiler executable.

Revision as of 16:23, 17 February 2024

Ferris holding his Dreamcast controller

WIP: This article is currently under construction. The repos linked to below are not yet live.

Rust is a systems programming language rising in popularity which emphasizes memory safety and performance. Due to its operating at a low level, it is an ideal candidate for running on the Dreamcast. Doing so presents a bit of a challenge, however, as the official Rust compiler is based on the LLVM toolchain infrastructure, which does not support the Dreamcast CPU's SuperH architecture. Dreamcast programming is instead done with GCC, the GNU Compiler Collection. There are currently two viable solutions to this challenge:

  • rustc_codegen_gcc: A libgccjit-based codegen backend for rustc (preferred method)
  • gccrs: a Rust frontend for GCC

Neither solution is complete at this time, and both are under active development. Using them on the Dreamcast should be considered experimental. rustc_codegen_gcc is quite further along, however, and is usable with some patience with limitations and rapid change. libcore and liballoc work, and KallistiOS bindings are planned. On the other hand, gccrs can compile for Dreamcast, but is in a very early stage, with much of the language unimplemented and no libcore support.

rustc_codegen_gcc

With rustc_codegen_gcc, we can interface the standard rustc compiler frontend with libgccjit, a GCC code-generation API. With the help of the Rust-for-Dreamcast repo and the kos-rs crate containing KallistiOS bindings, we can set up rustc_codegen_gcc to compile Rust programs with core and alloc support (but not the entirety of std). Rust-for-Dreamcast includes wrapper scripts to invoke rustc and cargo tools in a familiar way. The familiar borrow checker still works, and one can import and use no_std crates. Despite this support, rustc_codegen_gcc is still in active development, so if using such a setup, expect that things may change rapidly over time. At this time, there's not yet support for adding new architectures to Rust libraries and tools using rustc_codegen_gcc, and as mentioned before, those tools don't currently support SuperH, so support for customizing build options and proper linking is limited. We will use some workarounds. See the rustc_codegen_gcc progress reports for more information on the project's progress.

We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:

  • You must already have a KallistiOS development environment set up. This means you have created a cross-compiling toolchain for SH4 and you have built KallistiOS with it. See Getting Started with Dreamcast development for more information.
    • For the purposes of this guide, we will assume you are using the standard paths for Dreamcast development tools; i.e. your environment is set up in /opt/toolchains/dc.
    • Your KallistiOS installation will need its floating point precision setting set to m4-single. At this time, rustc_codegen_gcc will not compile with KallistiOS's default -m4-single-only setting. This setting can be changed in KallistiOS's environ.sh, but changing the setting may require you to rebuild your toolchain if you have not built it with m4-single support (which is off by default, but can be enabled in the config.mk file). Once you modify the setting in your environ.sh and re-source the environ.sh, you'll need to rebuild KallistiOS with a make clean and make for the changes to take effect. Keep in mind, however, that KallistiOS doesn't officially support -m4-single yet and so some things may be broken, especially libraries in kos-ports that haven't been heavily tested.
  • You must already have a relatively up-to-date Rust installation, either using your operating system's package manager or rustup.

Building a cross-compiling libgccjit.so for rustc_codegen_gcc

Before we can use rustc_codegen_gcc, we must compile libgccjit.so, the libgccjit library, for your system. This entails building a unique copy of the SH4 toolchain in its own directory under /opt/toolchains/dc/rust, using a forked version of GCC with enhancements made to libgccjit.

We will first clone the rust-for-dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using git, clone the rust-for-dreamcast repository to /opt/toolchains/dc/rust:

git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust

Enter your KallistiOS installation's dc-chain directory:

cd /opt/toolchains/dc/kos/utils/dc-chain

Clear out any existing build files:

make clean-keep-archives

Copy the necessary toolchain patches to your dc-chain setup:

cp /opt/toolchains/dc/rust/toolchain/*.diff patches/

Copy the rustc_codegen_gcc configuration file into place:

cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk

Make any desired changes to the configuration (e.g., change makejobs=-j2 to the number of CPU threads you'd like to use during compilation), and then compile the SH4 toolchain:

make build-sh4

When this command is completed successfully, a libgccjit.so will be installed to /opt/toolchains/dc/rust/sh-elf/lib/libgccjit.so.

Building rustc_codegen_gcc

Clone the rustc_codegen_gcc to your rust directory:

git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc

rustc_codegen_gcc needs a config.toml file that specifies the location of libgccjit.so. Let's write the the gcc-path to the location of our libgccjit.so library file in this file:

echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml

The rust-for-dreamcast repository contains scripts and wrappers to assist you in building rustc_codegen_gcc and using it in conjunction with cargo and rustc. We'll need to add the path to those scripts to our PATH environment variable:

export PATH="/opt/toolchains/dc/rust/bin:$PATH"

You may also want to add the above lines to your shell's startup file or else you'll need to run them every time you open a new shell.

Now we can use the included scripts to set up rustc_codegen_gcc. Various patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:

rcg-dc patch

Now we can prepare and build rustc_codegen_gcc!

rcg-dc prepare
rcg-dc build

If all went well, rustc_codegen_gcc will have built successfully. You'll be able to invoke rcg-dc to manage the rustc_codegen_gcc for Dreamcast installation, and you'll be able to invoke rustc for Dreamcast through a wrapper script command rustc-dc, and likewise with cargo and its wrapper cargo-dc.

Compiling individual modules into object files with rustc

To incorporate Rust source files into a standard KallistiOS Makefile-based project, you can use the rustc-dc wrapper. If we assume the Rust module file is named example.rs, you'll need to add example.o as an object file in your Makefile's OBJS = declaration. Additionally, you'll need to add the following lines so that make knows how to compile Rust modules into .o object files:

%.o: %.rs
	rustc-dc $< -o $@

Alternatively, you can add those lines to your KallistiOS Makefile.rules file to avoid having to place it in every project's Makefile.

An example "Hello, world!" program built in this style which also demonstrates basic C interoperation is included with the Rust-for-Dreamcast repository, located at examples/rustc-hello.

Creating a new project using Cargo

cargo-dc simplifies invoking cargo and creating Dreamcast crates. When using cargo in this setup, we will need to compile our program and all crate code into a static library .a file, and link it with a KallistiOS trampoline function to start the Rust code. Your Rust code will start with the function you specify as rust_main(). Once you cargo-dc build your Dreamcast code into a .a file, use cargo-dc link to automatically link it with this KallistiOS trampoline function and generate an ELF file. Instructions to do this follow.

First, let's clone the kos-rs repo:

git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs

Create a new crate using cargo-dc:

cargo-dc new example --lib

Change the crate to a static library in Cargo.toml by changing the crate-type as follows:

crate-type = ["staticlib"]

Add the kos-rs crate to your Cargo.toml file:

[dependencies]
kos = { package = "kos-rs",  path = "/opt/toolchains/dc/rust/kos-rs" }

Add the following function to your crate's src/lib.rs file:

#[no_mangle]
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {
    [...]
    return 0;
}

The rust_main() function will serve as the entry point to your Rust code.

An example "Hello, world!" style program built using kos-rs and cargo-dc is included with the Rust-for-Dreamcast repository, located at examples/cargo-hello. Type cargo-dc build to build the project, then cargo-dc link to link against KallistiOS and generate a cargo-hello.elf. Make sure you have your KallistiOS environ.sh sourced in your terminal before running the link command.

Integrating a Cargo project with a KallistiOS project

We can also build a crate based on kos-rs and integrate the Rust code with other C code and KOS libraries. An example rotating 3D cube program built using kos-rs and cargo-dc combined with a Makefile-based KallistiOS project is included with the Rust-for-Dreamcast repository, located at examples/rust_cube. Type cargo-dc build to build the project, then invoke make to build the KallistiOS project and link the Rust code within it.

In-progress/future goals

  • Create implementation for KOS/newlib in Rust's libc crate
  • Create implementation for Rust's std library using libc support
  • Expand bindings for KOS APIs in the kos-rs crate
  • Continue to evolve KOS/Dreamcast support alongside the maturation of rustc_codegen_gcc
    • Add sh-dreamcast as an official tier 3 Rust target
  • Expand cargo-dc functionality, including integrating Rust-based disc tools to generate disc images