インラインアセンブリ

極めて低レベルな技巧やパフォーマンス上の理由から、CPUを直接コントロールしたいと思う人もいるでしょう。 Rustはそのような処理を行うために、インラインアセンブリを asm! マクロによってサポートしています。

fn main() { // asm!(assembly template asm!(アセンブリのテンプレート // : output operands : 出力オペランド // : input operands : 入力オペランド // : clobbers : 破壊されるデータ // : options : オプション ); }
asm!(アセンブリのテンプレート
   : 出力オペランド
   : 入力オペランド
   : 破壊されるデータ
   : オプション
   );

asm のいかなる利用もフィーチャーゲートの対象です(利用するには #![feature(asm)] がクレートに必要になります)。 そしてもちろん unsafe ブロックも必要です。

メモ: ここでの例はx86/x86-64のアセンブリで示されますが、すべてのプラットフォームがサポートされています。

アセンブリテンプレート

アセンブリテンプレート のみが要求されるパラメータであり、文字列リテラル (例: "") である必要があります。

#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn foo() { unsafe { asm!("NOP"); } } // other platforms // その他のプラットフォーム #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] fn foo() { /* ... */ } fn main() { // ... foo(); // ... }
#![feature(asm)]

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn foo() {
    unsafe {
        asm!("NOP");
    }
}

// その他のプラットフォーム
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn foo() { /* ... */ }

fn main() {
    // ...
    foo();
    // ...
}

以後は、 feature(asm)#[cfg] は省略して示します。

出力オペランド、入力オペランド、破壊されるデータ、オプションはすべて省略可能ですが、省略する場合でも正しい数の : を書く必要があります。

#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn main() { unsafe { asm!("xor %eax, %eax" : : : "{eax}" ); } }
asm!("xor %eax, %eax"
    :
    :
    : "{eax}"
   );

空白も必要ではありません:

#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn main() { unsafe { asm!("xor %eax, %eax" ::: "{eax}"); } }
asm!("xor %eax, %eax" ::: "{eax}");

オペランド

入力と出力のオペランドは、 : "制約1"(式1), "制約2"(式2), ... というフォーマットに従います。 出力オペランドの式は変更可能な左辺値か、アサインされていない状態でなければなりません。

#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn add(a: i32, b: i32) -> i32 { let c: i32; unsafe { asm!("add $2, $0" : "=r"(c) : "0"(a), "r"(b) ); } c } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] fn add(a: i32, b: i32) -> i32 { a + b } fn main() { assert_eq!(add(3, 14159), 14162) }
fn add(a: i32, b: i32) -> i32 {
    let c: i32;
    unsafe {
        asm!("add $2, $0"
             : "=r"(c)
             : "0"(a), "r"(b)
             );
    }
    c
}

fn main() {
    assert_eq!(add(3, 14159), 14162)
}

もし本当のオペランドをここで利用したい場合、波括弧 {} で利用したいレジスタの周りを囲む必要があり、また、オペランドの特有のサイズを書く必要があります。 これは、どのレジスタを利用するかが重要となる、ごく低レベルのプログラミングで有用です。

#![feature(asm)] fn main() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] unsafe fn read_byte_in(port: u16) -> u8 { let result: u8; asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); result } }
let result: u8;
asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port));
result

破壊されるデータ

いくつかのインストラクションは異なる値を持っている可能性のあるレジスタを変更する事があります。 そのため、コンパイラがそれらのレジスタに格納された値が処理後にも有効であると思わないように、破壊されるデータのリストを利用します。

#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn main() { unsafe { // Put the value 0x200 in eax // eaxに0x200を格納します // asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "{eax}"); asm!("mov $$0x200, %eax" : /* 出力なし */ : /* 入力無し */ : "{eax}"); } }
// eaxに0x200を格納します
asm!("mov $$0x200, %eax" : /* 出力なし */ : /* 入力無し */ : "{eax}");

入力と出力のレジスタは変更される可能性があることが制約によってすでに伝わっているために、リストに載せる必要はありません。 それ以外では、その他の暗黙的、明示的に利用されるレジスタをリストに載せる必要があります。

もしアセンブリが条件コードを変更する場合、レジスタ cc も破壊されるデータのリストに指定する必要があります。 同様に、もしアセンブリがメモリを変更する場合 memory もリストに指定する必要があります。

オプション

最後のセクション、 options はRust特有のものです。 options の形式は、コンマで区切られた文字列リテラルのリスト(例: :"foo", "bar", "baz")です。 これはインラインアセンブリについての追加の情報を指定するために利用されます:

現在有効なオプションは以下の通りです:

  1. volatile - このオプションを指定することは、gcc/clangで __asm__ __volatile__ (...) を指定することと類似しています。
  2. alignstack - いくつかのインストラクションはスタックが決まった方式(例: SSE)でアラインされていることを期待しています。 このオプションを指定することはコンパイラに通常のスタックをアラインメントするコードの挿入を指示します。
  3. intel - デフォルトのAT&T構文の代わりにインテル構文を利用することを意味しています。
#![feature(asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn main() { let result: i32; unsafe { asm!("mov eax, 2" : "={eax}"(result) : : : "intel") } println!("eax is currently {}", result); }
let result: i32;
unsafe {
   asm!("mov eax, 2" : "={eax}"(result) : : : "intel")
}
println!("eax is currently {}", result);

さらなる情報

現在の asm! マクロの実装は LLVMのインラインアセンブリ表現 への直接的なバインディングです。 そのため破壊されるデータのリストや、制約、その他の情報について LLVMのドキュメント を確認してください。