この記事は Rust Advent Calendar 2022 14日目の記事です。

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

はじめに

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

 

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

 

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

 

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

 

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

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"

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

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 ができるようになります。

 

つまり、

$ cargo gccrs run --release

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

なお、最新の gccrs へ追従していなかったので、パッチをプルリクしたら、早速マージしていただけました。これをインストールするために、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 を返すようですので、いつものコードはこんな感じになります。

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() だけになりますが、それを定義するためにはファットポインタの定義などが必要になったり、…、結局、下記のようになりました。

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をみることができます。

 

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

マイルストン先週今週差分開始日終了日目標Data Structures 1 – Core100%100%-2020/11/302021/1/272021/1/29Control Flow 1 – Core100%100%-2021/1/282021/2/102021/2/26Data Structures 2 – Generics100%100%-2021/2/112021/3/142021/5/28Data Structures 3 – Traits100%100%-2021/5/202021/9/172021/8/27Control Flow 2 – Pattern Matching100%100%-2021/9/202021/12/92021/11/29Macros and cfg expansion100%100%-2021/12/12022/3/312022/3/28Imports and Visibility100%100%-2022/3/292022/7/132022/5/27Const Generics100%100%-2022/5/302022/10/102022/10/17Initial upstream patches100%100%-2022/10/102022/11/132022/11/13Upstream initial patchset78%79%+1%2022/11/13-2022/12/19Final set of upstream patches20%21%+1%2022/11/16-2023/4/30Intrinsics and builtins18%18%-2022/9/6-TBDBorrow checking0%0%-TBD-TBDConst Generics 20%0%-TBD-TBDRust-for-Linux compilation0%0%-TBD-TBD

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

おわりに

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

なお、GCC にコントリビュートする場合には、まず初めにこちらのドキュメントをしっかりと読んでから。

補足: 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 のブランチがまだマージされていないため、aarch64-apple-darwin21 は not supported と言われてしまいます。

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. ↩

Creative Commons Zero v1.0 Universal ↩

广告
将在 10 秒后关闭
bannerAds