在系统优化、OS或嵌入式开发及其他类型的低级编程过程中,有时可能需要变现原生的汇编程序。"内嵌汇编"提供了一种将某些汇编指令集成到Rust语法中然后输入寄存器以及将其输出直接引入到Rust 变量的简单方法。
Rust 的Ngihtly版中对"内嵌汇编"新引入了一种新的语法在在内侧。
Ngihtly Rust 支持"内嵌汇编"的(asm!) 的语法已有很长一段时间了。但是,该语法只是公开了一部分纯原始的LLVM 程序集,而且不支持开发人员安全使用的帮助措施。在这种枪口下,如果稍有错误,就会产生内部编译器错误(ICE),而不是友好的rustc异常警告。另外,这些原生的语法也不易读,容易出错(看上去就像是GCC的内联程序集语法,但存在细微差异(如寄存器约束中的名称)。该语法也基本上不支持任何非LLVM后端。由于这种种限制,虽然asm!是用户呼声最高的功能之一,但是不太适合迁移到稳定的Rust版本中。
为了改进asm!让更多用户可以使用,Amanieu d'Antras新设计和实现了一种全新的、更友好的语法。从概念到编译器实现,新语法还有很长的路要走:
该提案最初是作为内部RFC的前一项。
为内嵌汇编成为语言团队的第一项目组之一,并在项目组仓库中反复设计和讨论。
RFC 2873(仍在讨论中)为asm!语法及其和Rust语言的交互提供了规范。
已将老的asm!重命名为llvm_asm!(鉴于此语法脆弱的 ICE 快乐特性,最终计划会删除此语法,但在评估新语法时,希望旧语法可用于比较和替代方法。)
PR 69171(也是由 Amanieu)在夜间实现了新asm!的语法。
下面是使用新的内嵌汇编语法,使用 x86-64 Linux 上的直接写入系统调用将消息打印到标准输出的示例:
#![feature(asm)]
fn main() {let buf = "Hello from asm!\n";let ret: i32;unsafe {asm!("syscall",in("rax") 1, // syscall numberin("rdi") 1, // fd (stdout)in("rsi") buf.as_ptr(),in("rdx") buf.len(),out("rcx") _, // clobbered by syscallsout("r11") _, // clobbered by syscallslateout("rax") ret,);}println!("write returned: {}", ret);}
注:该实例可以直接在线上playground运行和测试。
上面的示例指定了Linux系统通过寄存器调用特定的确切的输入、输出。我们还可以通过任意寄存器提供输入和输出,编译器将为我们选择适当的寄存器。下面的示例使用位操作指令计算值中所有设置位的位数,并将它们存储在内存切片中:
#![feature(asm)]
fn main() {let mut bits = [0u8; 64];for value in 0..=1024u64 {let popcnt;unsafe {asm!("popcnt {popcnt}, {v}2:blsi rax, {v}jz 1fxor {v}, raxtzcnt rax, raxstosbjmp 2b1:",v = inout(reg) value => _,popcnt = out(reg) popcnt,out("rax") _, // scratchinout("rdi") bits.as_mut_ptr() => _,);}println!("bits of {}: {:?}", value, &bits[0..popcnt]);}}
注:该实例可以直接在线上playground运行和测试。该代码主要用于演示内联程序集,而不是演示任何特定算法实现的有效性。可注意到已为value和popcnt选择了寄存器,同时bits.as_mut_ptr()必须转到rdi寄存器中,以便使用stosb指令。
此外在 x86 平台上,默认情况下使用英特尔语法;但是,asm!可以通过option(att_syntax)使用AT&T语法。将已有的内嵌汇编代码转换为asm!新语法时,可使用这个语法。
有关新语法的完整详细信息,请参阅Rust官方RFC 2873。如果已经使用asm!编写代码的建议请将现有内联程序转换为新语法,如果发现问题和bug请及时上报。