使用Rust内联汇编设置非默认的舍入模式不被LLVM优化器所尊重?

5

我正在开发一个Rust crate,它可以改变舍入模式(+inf、-inf、nearest或truncate)。

改变舍入模式的函数是使用内联汇编编写的:

fn upward() {
    let cw: u32 = 0;
    unsafe {
    asm!("stmxcsr $0;
          mov $0, %eax;
          or $$0x4000, %eax;
          mov %eax, $0;
          ldmxcsr $0;"
          : "=*m"(&cw)
          : "*m"(&cw)
          : "{eax}"
        );
    }
}

当我以调试模式编译代码时,它按预期工作,对于四舍五入到正无穷大的一个三分之一,我得到0.3333333333337,但是当我以发布模式编译时,无论我设置什么舍入模式,我都会得到相同的结果。我猜测这种行为是由LLVM后端进行的优化所致。

如果我知道哪些LLVM passes负责此优化,我可以禁用它们,因为目前我没有看到其他解决方法。


我担心这个信息非常依赖于LLVM的版本(可以自由添加/删除传递),因此与rustc的版本相关联。您使用的是哪个版本的rustc?如果升级时出现问题,您介意吗? - Matthieu M.
我正在使用 Rust 1.10 nightly。我不介意它是否会出问题。只要我能理解是什么导致了这种行为,我可以用一点努力来制定一些解决方法。 - Houss_gc
1
经过一些阅读,我认为有一些调度传递正在将除法指令移动到upward()函数调用之前(只是猜测),如果我错了,请纠正我。 - Houss_gc
请包含您正在测试的确切代码:如果您所说的“三分之一”字面上意味着表达式1.0/3.0,那么当优化开启时,这将在编译时进行评估(即使用LLVM在编译时选择的任何舍入模式)。如果您以优化器无法看透的方式计算“三分之一”(例如解析命令行参数),那么问题变得更加困难。 - huon
@huon 这是我正在使用进行测试的code。 我可以强制LLVM在编译时使用某些舍入模式吗? - Houss_gc
1个回答

5

基本上,你做不到这一点。LLVM 假定所有浮点运算都使用默认舍入模式,并且浮点控制寄存器从不被读取或修改。

最近在 LLVM-dev 邮件列表上讨论了这个问题(链接),如果您感兴趣的话。

与此同时,唯一可靠的解决方法是使用内联汇编,例如 asm!("addsd $0, $1"

Rust 的标准库也假定您不会修改舍入模式(特别是用于浮点数和字符串之间转换的代码对此敏感)。


如果我理解正确的话,使用内联汇编时,mxcsr或fctrl寄存器设置的舍入模式会被asm!宏中的计算考虑在内? - Houss_gc
2
是的。此时,您基本上只是编写原始汇编代码,因此 Rust 或 LLVM IR 的语义并不重要。 - Eli Friedman
当然,LLVM优化是在IR上进行的,谢谢。 - Houss_gc

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