inline-assembly.md
commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d
為了極端底層操作和性能要求,你可能希望直接控制 CPU。Rust 通過asm!
宏來支持使用內(nèi)聯(lián)匯編。語法大體上與 GCC 和 Clang 相似:
asm!(assembly template
: output operands
: input operands
: clobbers
: options
);
任何asm
的使用需要功能通道(需要在包裝箱上加上#![feature(asm)]
來允許使用)并且當(dāng)然也需要寫在unsafe
塊中
注意:這里的例子使用了 x86/x86-64 匯編,不過所有平臺都受支持。
assembly template
是唯一需要的參數(shù)并且必須是原始字符串(就是""
)
#![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]
從現(xiàn)在開始將被忽略。)
輸出操作數(shù),輸入操作數(shù),覆蓋和選項都是可選的,然而如果你要省略它們的話,你必選加上正確數(shù)量的:
:
# #![feature(asm)]
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
# fn main() { unsafe {
asm!("xor %eax, %eax"
:
:
: "{eax}"
);
# } }
有空格在中間也沒關(guān)系:
# #![feature(asm)]
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
# fn main() { unsafe {
asm!("xor %eax, %eax" ::: "{eax}");
# } }
輸入和輸出操作數(shù)都有相同的格式:: "constraints1"(expr1), "constraints2"(expr2), ..."
。輸出操作數(shù)表達(dá)式必須是可變的左值,或還未賦值的:
# #![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)
}
如果你想在這里使用真正的操作數(shù),然而,要求你在你想使用的寄存器上套上大括號{}
,并且要求你指明操作數(shù)的大小。這在非常底層的編程中是很有用的,這時你使用哪個寄存器是很重要的:
# #![feature(asm)]
# #[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
# }
一些指令修改的寄存器可能保存有不同的值,所以我們使用覆蓋列表來告訴編譯器不要假設(shè)任何裝載在這些寄存器的值是有效的。
# #![feature(asm)]
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
# fn main() { unsafe {
// Put the value 0x200 in eax
asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "{eax}");
# } }
輸入和輸出寄存器并不需要列出因為這些信息已經(jīng)通過給出的限制溝通過了。因此,任何其它的被使用的寄存器應(yīng)該隱式或顯式的被列出。
如果匯編修改了代碼狀態(tài)寄存器cc
則需要在覆蓋中被列出,如果匯編修改了內(nèi)存,memory
也應(yīng)被指定。
最后一部分,options
是 Rust 特有的。格式是逗號分隔的基本字符串(也就是說,:"foo", "bar", "baz"
)。它被用來指定關(guān)于內(nèi)聯(lián)匯編的額外信息:
目前有效的選項有:
__asm__ __volatile__ (...)
# #![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);
# }
目前asm!
的實現(xiàn)是一個LLVM內(nèi)聯(lián)匯編表達(dá)式的直接綁定,所以請確保充分的閱讀他們的文檔來獲取關(guān)于覆蓋,限制等概念的更多信息。