有没有一种方法可以使Rust将指针视为非别名,以便它可以将它们标记为LLVM优化器的“noalias”?

26

以下是指针别名的示例:

pub unsafe fn f(a: *mut i32, b: *mut i32, x: *const i32) {
  *a = *x;
  *b = *x;
}

经过 -C opt-level=s 编译,会生成以下汇编代码:

example::f:
        push    rbp
        mov     rbp, rsp
        mov     eax, dword ptr [rdx]
        mov     dword ptr [rdi], eax
        mov     eax, dword ptr [rdx]
        mov     dword ptr [rsi], eax
        pop     rbp
        ret

请注意x被解引用了两次,LLVM没有将其视为noalias。我的第一个想法是避免在赋值中使用指针,而是使用安全的引用(因为这些引用“遵循LLVM的作用域noalias模型”),以提示优化器:

pub fn g(a: *mut i32, b: *mut i32, x: *const i32) {
  let safe_a = unsafe { &mut *a };
  let safe_b = unsafe { &mut *b };
  let safe_x = unsafe { &*x };
  *safe_a = *safe_x;
  *safe_b = *safe_x;
}

但是不幸的是,这会产生完全相同的结果。 safe_x 仍会被解引用两次。

我知道这个示例代码很愚蠢。参数可以很容易地更改为&i32 / &mut i32,或者我可以只解引用x一次,并将其存储在临时变量中,该变量用于赋值。这里的代码只是一个超级简单的别名测试,我对我的问题所问的更广泛的情况感兴趣。


2
有趣的是,如果我使用&mut i32、&mut i32、&i32作为原型,那么noalias属性将附加到x上。我似乎记得noalias不会被主动传递给LLVM,因为存在需要先澄清unsafe语义,然后再应用优化的担忧,以免LLVM“破坏”代码。 - Matthieu M.
可能相关:Unique<T> - Lukas Kalbertodt
2
记录一下,我在这里报告了优化不足的问题 issue #38941 - Cornstalks
2
@MatthieuM:您是否在考虑[问题#31681](https://github.com/rust-lang/rust/issues/31681)?确实,由于LLVM中的错误(最近已经修复),一些引用未标记为“noalias”,但并非所有引用都是如此。有些仍然可以标记为“noalias”。 - Cornstalks
1个回答

3

如果需要安全引用,请将其包装在函数或闭包中:

pub unsafe fn f(a: *mut i32, b: *mut i32, x: *const i32) {
    (|safe_a: &mut i32, safe_b: &mut i32, safe_x: &i32| {
        *safe_a = *safe_x;
        *safe_b = *safe_x;
    })(&mut *a, &mut *b, &*x)
}

这会产生所需的非锯齿行为:
example::f:
        movl    (%rdx), %eax
        movl    %eax, (%rdi)
        movl    %eax, (%rsi)
        retq

函数/闭包是不必要的。你所做的只是将原始指针转换为引用。这并不真正展示如何将指针视为非别名,而只是展示了如何将指针转换为引用。 - Shepmaster
@Shepmaster 这不是真的 - 比较汇编代码。在你的例子中,safe_x 被加载了两次 - 这证明了引用 不是 被认为是非别名的。闭包是必要的。 - orlp
嗯,我明白你的意思。那似乎是一个错误(限制?)按照定义,引用不应该有别名。在寻找问题时,我发现了由OP提交的问题! - Shepmaster
是的,抱歉,我应该在我的OP中更明确地表述(这就是我所说的“参数可以很容易地改为&i32/&mut i32”以及Matthieu M.评论所指的内容)。目前,据我所知,noalias仅适用于参考函数参数。尽管如此,我想这也是一个完全有效的答案。 - Cornstalks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接