我最近写了这个相当简单的asm函数
#[inline(always)]
pub fn usize_raw_load_acquire(dst: &mut usize, src: *const usize) {
use std::arch::asm;
debug_assert!(src.is_aligned());
debug_assert!(!src.is_null());
#[cfg(target_arch = "x86_64")]
unsafe {
// In x86, things are properly ordered by default, and these operations
// are atomic!
asm! {
"mov rax, [{src}]",
"mov [{dst}], rax",
src = in(reg) src,
dst = in(reg) dst,
out("rax") _,
options(nostack, preserves_flags),
}
}
#[cfg(not(target_arch = "x86_64"))]
compile_error!("unsupported arch");
}
有趣的(也许)是我已将其标记为安全......
由于很多原因,指针读取都很可怕,但我看不出这些原因在这里如何适用!不过,让我们只关注出处部分,并忽略我关于此代码的作用的各种其他断言。
本质上,我相信
src
可以是指向任何东西的指针......并且可以在任何环境中安全地读取(例如,即使&mut
存在于同一对象)
为什么我会错?
即使同一个对象存在
&mut
这部分肯定是错误的。 Rust 不仅仅在抽象机中定义
&mut
引用的唯一性;它也在其外部定义 - 例如,FFI 也必须遵守它们,内联汇编也必须遵守。
特别是,关于别名规则的唯一定义是它们至少遵循LLVM的noalias
这表明在函数执行期间,通过基于参数或返回值的指针值访问的内存位置也不会通过不基于参数或返回值的指针值访问。您可以看到它是根据
内存访问定义的,而不是IR读取。
在实际方面,LLVM 可能会基于未读取值的假设来优化其他代码(例如,覆盖存储的值),这可能会破坏其他代码和/或您的代码。另请参阅
UCG#331 - 内联汇编读取是否可以读取 uninit 并违反 noalias?.
其他不变量更多可能还可以。如果你传递一个无效的指针,处理器会陷入困境,但它不在抽象机内,所以 Rust 不会对此做出假设。这就像您(通过 FFI)调用一种没有 UB 的语言。