[第83回の様子]
2023/06/28に第83回を開催した。
内容としてはRust By Example 日本語版22. 安全でない操作の「22.1. Inline assembly」のClobbered registersの続きに取り組んだ...が、今週も終わらず。来週も続きをやる。
参加者は自分を入れて8人。最近は参加者がたくさんいて嬉しい。
[学んだこと]
- 22.1. Inline assembly: Clobbered registers
- 前回のサンプルコードをあらためて動かし、読み直す時間をとった。
use std::arch::asm;
fn main() {
// 4バイト×3つ分
let mut name_buf = [0_u8; 12];
// 文字列はASCIIコードとしてebx, edx, ecxに保管される
// ebxは予約されているので、その値を保存するためにpush/popが必要
unsafe {
asm!(
"push rbx",
"cpuid",
"mov [rdi], ebx",
"mov [rdi + 4], edx",
"mov [rdi + 8], ecx",
"pop rbx",
// Rustコードをシンプルにするために、アセンブリのコードを増やして
// 配列へのポインタを用いて値を保存する
// `out("ecx") val`のように明示的な出力レジスタを利用するのと対照的に、
// アセンブリの動きはより明示的になる
// ポインタそのものはあくまで入力として扱われる
in("rdi") name_buf.as_mut_ptr(),
// cpuid 0を選択し、eaxをclobberedに指定する
inout("eax") 0 => _,
// cpuidは以下のレジスタもclobberする
out("ecx") _,
out("edx") _,
);
}
// バイト列を文字列に変換
let name = core::str::from_utf8(&name_buf).unwrap();
println!("CPU Manufacturer ID: {}", name);
// CPU Manufacturer ID: AuthenticAMD
}
- レジスタに詳しい人があらためて解説をしてくれた内容を思い出しつつ書いてみると...
- cpuidと言う命令はebx、edx、ecxのレジスタに結果を書き込む
- ebxのbはベースレジスタの略。下のページ参照。
- ebxは4バイトのレジスタで、これを含む8バイトのレジスタがrbx
- なので、上記サンプルコードでは、push rbxを実行することでebxレジスタの値を保存している、と言うのをようやく理解できた
- レジスタのサイズと構造体のサイズの関係がパフォーマンス上重要になる
- C言語でパフォーマンスを気にする場合、メモリのレイアウトとかアラインメントを意識する必要がある
- Rust側でC言語と同じメモリレイアウトを使う場合、#[repr(C)]のように記述する
- この例は以前FFIについて学んだ際に登場していたが理解できていなかった
#[repr(C)]
#[derive(Clone, Copy)]
struct Complex {
re: f32,
im: f32,
}
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
allocの話になった時に爆笑してる人がいて謎。楽しんでくれてるならヨシ。
今週のプルリクエストはおやすみ。