GCC Rust がメインラインに入ったらしいので触ってみた

この記事は [Rust Advent Calendar 2022](https://qiita.com/advent-calendar/2022/rust) 14日目の記事です。

:::note
記事を書いているうちに、GCC Rust パッチセット第１弾がメインラインにマージされていたので、タイトルも含め、いくつか修正＆追記しておきました。（2022/12/15）
:::

# はじめに

2022年12月11日、ついに Rust support 第１弾がマージされた Linux Kernel 6.1 が正式リリースされました。今回は、長いカーネルシンボル名のサポート、Rust コードのビルドルールやスクリプト、Rustカーネルアロケータやprintk周りのサポート、Rustで書かれたカーネルモジュールのサポートなど、最小限の基盤的なコードがマージされました。

https://lore.kernel.org/lkml/CAHk-=wj_HcgFZNyZHTLJ7qC2613zphKDtLh6ndciwopZRfH0aQ@mail.gmail.com/

https://github.com/Rust-for-Linux/linux

既に Rust support 第２弾が提案されており、Rust での Linux カーネルの開発が現実的なものとなり、低レイヤ好きの Rustecean の活躍の場が大きく拡がります、たぶんね。

https://lore.kernel.org/lkml/20221110164152.26136-1-ojeda@kernel.org/

それと同じくらいに期待しているのが、GCC Front-End for Rust (GCC Rust; コマンド名 `gccrs`) の GCC（Gnu Compiler Collection）へのマージの方針が GCC Steering Committee によって承認され、パッチセット第１弾が GCC のメインラインへマージされたというニュースです。

https://www.phoronix.com/news/GCC-Rust-SC-Approved

https://www.phoronix.com/news/GCC-13-Rust-Merged

将来的に Rust-for-Linux が LLVM ベースの clang + rustc だけではなく、GCC ベースの gcc + gccrs でもビルドできる可能性があり、OSS の安全保障上からも（ちょっとおおげさ？）重要な営みでしょう。また、バックエンドが LLVM から GCC になることで、GCC がサポートする豊富な CPU アーキテクチャへの対応が可能となるなどのメリットが期待できます。

https://github.com/Rust-GCC/gccrs

GCC Rust は C++ で記述されており、まだまだ実験的なコンパイラで、今後多くの変更が加えられるでしょうし、実際の Rust プログラムをコンパイルするには時期尚早というところです。ですが、Rust も Swift も Go も、言語処理系で開発して楽しい部分は残ってないよね、とお嘆きのあなた、いかがでしょうか？

ということで、ちょっとだけ触ってみました。

# GCC Rust のビルド

開発環境としては Ubuntu がおすすめです。22.04 LTS で良いと思います。まずは、ビルドするためのパッケージをインストールしましょう。基本的には GNU C Compiler をビルドするために必要なパッケージ群となります。

```
$ sudo apt install build-essential libgmp3-dev libmpfr-dev libmpc-dev flex bison autogen dejagnu
```

次に、レポジトリをクローンします。GCC のような大規模なプロジェクトでフルクローンするとストレージを圧迫するので、パーシャルクローン（ブロブレスクローン）でやってみます。

```
$ git clone --filter=blob:none https://github.com/Rust-GCC/gccrs.git
```

:::note
GCCのメインラインからビルドする場合には、`https://gcc.gnu.org/git/gcc.git` や `https://github.com/gcc-mirror/gcc.git` からクローンして下さい。
:::

ビルドはソースツリーの外で行いましょう。

```
$ mkdir gccrs-build
$ cd gccrs-build
$ ../gccrs/configure --prefix=$HOME/gccrs-install --disable-bootstrap --enable-multilib --enable-languages=rust
$ make -j6
```

`-j6` のところはマシンのコア数やメモリサイズに合わせて適当に。`make check-rust` でテストがだいたい通れば OK です。インストールして、パスを通しましょう。

```
$ make install
$ export PATH="$HOME/gccrs-install/bin:$PATH"
```

まずは、次のようなプログラムで、動作を確認してみましょう。

```rust:test.rs
fn main() {}
```

起動します。

```
$ gccrs -O2 test.rs -o test
rust1: fatal error: gccrs is not yet able to compile Rust code properly. Most of the errors produced will be gccrs' fault and not the crate you are trying to compile. Because of this, please reports issues to us directly instead of opening issues on said crate's repository.

Our github repository: https://github.com/rust-gcc/gccrs
Our bugzilla tracker: https://gcc.gnu.org/bugzilla/buglist.cgi?bug_status=__open__&component=rust&product=gcc

If you understand this, and understand that the binaries produced might not behave accordingly, you may attempt to use gccrs in an experimental manner by passing the following flag:

`-frust-incomplete-and-experimental-compiler-do-not-use`

or by defining the following environment variable (any value will do)

GCCRS_INCOMPLETE_AND_EXPERIMENTAL_COMPILER_DO_NOT_USE

Forcargo-gccrs, this means passing

GCCRS_EXTRA_FLAGS="-frust-incomplete-and-experimental-compiler-do-not-use"

as an environment variable.
compilation terminated.
```

おやおや、怒られてしまいました。不具合の報告先とリスクを理解してから、

```
$ export GCCRS_INCOMPLETE_AND_EXPERIMENTAL_COMPILER_DO_NOT_USE=
```

としてから使って下さい、ということだそうです。このおまじないで、Rust プログラムが普通にコンパイルできるようになります。

# cargo-gccrs のインストール

Rust 単独のプロジェクトであれば、やはり `cargo` を使いたくなります。`cargo-gccrs` を使うと、`rustc` の代わりに `gccrs` で `build`, `run`, `test` ができるようになります。

https://github.com/Rust-GCC/cargo-gccrs

つまり、

```
$ cargo gccrs run --release
```

とすることで、gccrs を使って、最適化フラグをオンにして、パッケージをビルドしてランすることができるようになります。

なお、最新の `gccrs` へ追従していなかったので、[パッチをプルリク](https://github.com/Rust-GCC/cargo-gccrs/pull/64)したら、早速マージしていただけました。これをインストールするために、GitHub からインストールします。`cargo-gccrs` は Rust で書かれていて、現時点では `rustc` を使わないとビルドできません。

```
$ cargo install --git https://github.com/Rust-GCC/cargo-gccrs cargo-gccrs
```

# Rust プログラムをコンパイル

GCC Rust には std ライブラリや core ライブラリがまだ同梱されておらず、no_std, no_core の状態となりますので、Cライブラリの力を借りつつ、core ライブラリ相当も書いてやる必要があります。`main()` は `i32` を返すようですので、いつもの[コード](https://qiita.com/moriai/items/b1fa7d1b43d985d408cc#標準的なターゲットへは)はこんな感じになります。

```rust:src/main.rs
mod core;

extern "C" {
    fn write(fd: usize, buf: *const u8, len: usize);
}

fn main() -> i32 {
    unsafe {
        let msg = "Hello, World!\n";
        let buf = msg.as_ptr();
        let len = msg.len();
        write(1, buf, len);
    }
    0
}
```

no_core な環境で基本データ型 `str` のメソッドさえも定義されていないので、これらのメソッドを GCC Rust のテストプログラム[^1]から拝借して書いてみます。とりあえず必要になるのは `as_ptr()` と `len()` だけになりますが、それを定義するためにはファットポインタの定義などが必要になったり、...、結局、下記のようになりました。

```rust:src/core.rs
mod mem {
    extern "rust-intrinsic" {
        #[rustc_const_stable(feature = "const_transmute")]
        fn transmute<T, U>(_: T) -> U;
    }
}

struct FatPtr<T> {
    data: *const T,
    len: usize,
}

pub union Repr<T> {
    rust: *const [T],
    rust_mut: *mut [T],
    raw: FatPtr<T>,
}

impl<T> [T] {
    pub const fn len(&self) -> usize {
        unsafe { Repr { rust: self }.raw.len }
    }
}

impl str {
    pub const fn as_ptr(&self) -> *const u8 {
        self as *const str as *const u8
    }

    pub const fn as_bytes(&self) -> &[u8] {
        unsafe { mem::transmute(self) }
    }

    pub const fn len(&self) -> usize {
        self.as_bytes().len()
    }
}
```

あとは、`Cargo.toml` をさらっと書いて、`run` してみます。

```
$ cargo gccrs run
   Compiling hello v0.1.0 (/home/xxx/Misc/hello)
src/core.rs:8:1: warning: struct is never constructed: ‘FatPtr’
    8 | struct FatPtr<T> {
      | ^
    Finished dev [unoptimized + debuginfo] target(s) in 0.14s
     Running `target/debug/hello`
Hello, World!
```

ちょっと warning が出ていますが、無事に動きました。

# GCC Rust の現状

こちらを見ると、monthly と weekly のプロジェクトの進捗レポート[^2]をみることができます。

https://github.com/Rust-GCC/Reporting

2022年12月12日の weekly レポートをちらっと覗いてみましょう。


| マイルストン | 先週 | 今週 | 差分 | 開始日 | 終了日 | 目標 |
|-----------------------------------|-----------|-----------|-------|------------|------------|------------|
| Data Structures 1 - Core          |      100% |      100% | -     | 2020/11/30 | 2021/1/27  | 2021/1/29  |
| Control Flow 1 - Core             |      100% |      100% | -     | 2021/1/28  | 2021/2/10  | 2021/2/26  |
| Data Structures 2 - Generics      |      100% |      100% | -     | 2021/2/11  | 2021/3/14  | 2021/5/28  |
| Data Structures 3 - Traits        |      100% |      100% | -     | 2021/5/20  | 2021/9/17  | 2021/8/27  |
| Control Flow 2 - Pattern Matching |      100% |      100% | -     | 2021/9/20  | 2021/12/9  | 2021/11/29 |
| Macros and cfg expansion          |      100% |      100% | -     | 2021/12/1  | 2022/3/31  | 2022/3/28  |
| Imports and Visibility            |      100% |      100% | -     | 2022/3/29  | 2022/7/13  | 2022/5/27  |
| Const Generics                    |      100% |      100% | -     | 2022/5/30  | 2022/10/10 | 2022/10/17 |
| Initial upstream patches          |      100% |      100% | -     | 2022/10/10 | 2022/11/13 | 2022/11/13 |
| Upstream initial patchset         |       78% |       79% | +1%   | 2022/11/13 | -          | 2022/12/19 |
| Final set of upstream patches     |       20% |       21% | +1%   | 2022/11/16 | -          | 2023/4/30  |
| Intrinsics and builtins           |       18% |       18% | -     | 2022/9/6   | -          | TBD        |
| Borrow checking                   |        0% |        0% | -     | TBD        | -          | TBD        |
| Const Generics 2                  |        0% |        0% | -     | TBD        | -          | TBD        |
| Rust-for-Linux compilation        |        0% |        0% | -     | TBD        | -          | TBD        |


このところは、GCC 13 のマージウィンドウに向けたパッチセットの開発に注力しているようですが、メインラインにマージされたようですので、Upstream initial patchset のマイルストンは次週は完了フラグとなると思われます。個人的に気になる Rust-for-Linux compilation というマイルストンもありますね。Borrow checking がこれからということで、コンパイラ本体の開発に大きなマイルストンが残っていますし、core や std のライブラリの開発という大きな山も残っています。

# おわりに

GCC 用の Rust フロントエンドについて、軽く紹介してみました。実用にはまだまだですが、逆にコントリビュートの余地が広く残されていますので、「我こそは」という方、ぜひ、よろしくお願いします。人によっては、クリスマスから17連休という噂もあり...

なお、GCC にコントリビュートする場合には、まず初めに[こちらのドキュメント](https://gcc.gnu.org/contribute.html)をしっかりと読んでから。

[^1]: Copyright (C) 1998-2022 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved.

[^2]: Creative Commons Zero v1.0 Universal

# 補足: macOS でのビルド

macOS Big Sur on x86_64 でビルドするためには、Homebrew などでビルドに必要なパッケージをインストールしてから、いくつか引数を追加して `configure` を起動する必要があります。macOS の `clang++` では GCC Rust のコンパイルに失敗するので、`g++-12` が必要です。下記の例では、Homebrew のインストール先を `/usr/local` としていますので、それぞれの環境に合わせて修正してみてください。

```
$ brew update
$ brew install gcc-12 gmp mpfr libmpc autogen dejagnu
$ CC=gcc-12 CXX=g++-12 ../gccrs/configure \
--build=x86_64-apple-darwin20 \
--prefix=$HOME/Work/gccrs-install \
--disable-bootstrap \
--enable-languages=rust \
--enable-multilib \
--with-native-system-header-dir=/usr/include \
--with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \
--with-gmp=/usr/local \
--with-mpfr=/usr/local \
--with-mpc=/usr/local
...
...
$ make -j6
...
...
$ make install
...
...

```

ちなみに、[AArch64 (Arm64) on Darwin のブランチ](https://github.com/iains/gcc-12-branch)がまだマージされていないため、aarch64-apple-darwin21 は not supported と言われてしまいます。