Rust 内联汇编详解
Rust 通过 asm! 宏提供内联汇编支持。这允许我们在编译器生成的汇编输出中嵌入手写的汇编代码。通常情况下并不需要这么做,但在需要极致性能或精确时序控制时,这是唯一的选择。访问底层硬件原语(例如内核代码中)也往往需要此功能。
注意:示例基于 x86/x86-64 架构,但其他架构同样受支持。
目前内联汇编支持以下架构:
- x86 和 x86-64
- ARM
- AArch64
- RISC-V
基本用法
我们从最简单的例子开始:
use std::arch::asm;
unsafe {
asm!("nop");
}
这将把 NOP(空操作)指令插入编译器生成的汇编中。注意,所有 asm! 调用都必须在 unsafe 块内,因为它们可能插入任意指令并破坏各种不变量。要插入的指令作为字符串字面量列在 asm! 宏的第一个参数中。
输入与输出
只插入一条无操作的指令略显枯燥,让我们来做点真正处理数据的事情:
use std::arch::asm;
let x: u64;
unsafe {
asm!("mov {}, 5", out(reg) x);
}
assert_eq!(x, 5);
这段代码将值 5 写入 u64 变量 x。你会发现用于指定指令的字符串字面量实际上是一个模板字符串。它遵循与 Rust 格式化字符串相同的规则。不过,插入到模板中的参数看起来可能有些陌生。
首先我们需要指定变量是输入还是输出。在这里它是输出,我们通过写 out 来声明。我们还需要指定汇编期望变量位于哪种寄存器中。这里我们将其放在任意通用寄存器中,通过指定 reg 实现。编译器会自动选择合适的寄存器插入到模板中,并在内联汇编执行完毕后从该寄存器读取变量。
再看一个同时使用输入的示例:
use std::arch::asm;
let i: u64 = 3;
let o: u64;
unsafe {
asm!(
"mov {0}, {1}",
"add {0}, 5",
out(reg) o,
in(reg) i,
);
}
assert_eq!(o, 8);
这会将变量 中的输入加 5,并将结果写入变量 。这个汇编的具体做法是先将 的值复制到输出,然后加 5。

