第84回です。諸事情あって1週スキップしてました。前回はこちら。
[第84回の様子]
2023/07/12に第84回を開催した。
内容としてはRust By Example 日本語版22. 安全でない操作の「22.1. Inline assembly」のClobbered registersをなんとか終わらせた。1ヶ月以上かかってしまった...。
参加者は自分を入れて6人。
[学んだこと]
- 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の順にCPUの製造者IDが格納される
- b->d->cの順が謎だがそういうものらしい
- eaxにも影響があり、アセンブリによって該当レジスタが変更されているので、インラインアセンブリの前後でRustコンパイラが自動修復できるように、inout("eax") 0 => _,のように宣言している
- ebxレジスタにも影響があり、Rustコンパイラに自動修復してほしいところだが、これはLLVMによって予約されており、インラインアセンブリで入出力のレジスタとして指定することができない
- そのため、インラインアセンブリで独自にpush/popを実行する必要がある
- 試しに、以下のようにebxを出力レジスタに指定するとコンパイルエラーとなる
use std::arch::asm;
fn main() {
let mut name_buf = [0_u8; 12];
unsafe {
asm!(
"cpuid",
"mov [rdi], ebx",
"mov [rdi + 4], edx",
"mov [rdi + 8], ecx",
in("rdi") name_buf.as_mut_ptr(),
inout("eax") 0 => _,
// ebxレジスタを出力に利用
out("ebx") _,
out("ecx") _,
out("edx") _,
);
}
let name = core::str::from_utf8(&name_buf).unwrap();
println!("CPU Manufacturer ID: {}", name);
}
// 以下のコンパイルエラー
error: cannot use register `bx`: rbx is used internally by LLVM and cannot be used as an operand for inline asm
--> src/main.rs:15:13
|
15 | out("ebx") _,
| ^^^^^^^^^^^^
error: could not compile `playground` (bin "playground") due to previous error
use std::arch::asm;
// シフト演算と加算を利用してxに6をかける
let mut x: u64 = 4;
unsafe {
asm!(
"mov {tmp}, {x}",
"shl {tmp}, 1",
"shl {x}, 2",
"add {x}, {tmp}",
x = inout(reg) x,
tmp = out(reg) _,
);
}
assert_eq!(x, 4 * 6);
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
ようやくClobbered registersの森を抜けた...インラインアセンブリの道のりは遠い。
今週もプルリクエストはおやすみ。