13

我有一个函数,其中 Rust/LLVM 的优化失败并导致恐慌(在发布版本中),而未优化的代码(调试版本)工作正常。如果我比较生成的汇编代码,我什至无法理解优化器试图完成什么。(一个原因可能是这个函数使用了内联汇编器。)

有什么方法可以告诉 Rust 在优化过程中不理会某些功能,还是我必须关闭所有优化?

下面是具体功能:

#[naked]
pub extern "C" fn dispatch_svc(){
    Cpu::save_context();
    let mut nr: u32 = 0;
    unsafe {
        asm!("ldr r0, [lr, #-4]
              bic $0, r0, #0xff000000":"=r"(nr)::"r0":"volatile")
    };
    swi_service_routine(nr);
    Cpu::restore_context_and_return();
}
4

2 回答 2

8

据我所知,除了整个 crate 之外,Rust 没有任何工具可以指定任何优化级别。您唯一的解决方法是在单独的 crate 中编译此函数,编译它,然后将其作为预编译的依赖项包含在内。(正常的 rust-dependencies 是在依赖的优化级别编译的)

但是:为这个单一功能指定不同的优化级别并不能解决您的问题!当然,它今天可能工作,但每次编译器(或优化标志)更改时都会再次中断。

TL;DR:裸函数是非常不安全的(尊敬的,你是一个比我更勇敢的人!)。使用它们的唯一可靠方法是只编写一个asm!()块作为整个函数体,仅此而已。像你正在做的混合asm!,普通的 Rust 和函数调用实际上是未定义的行为(在可怕的 C/Nasal-Demon 术语中)没有多少优化调整会改变这一点。


在 Rust 的作者“做对了”之前,裸函数仍然不稳定。正如您所发现的,这存在许多微妙的问题。在此处跟踪稳定问题

裸 fn RFC中,在“动机”下,我们发现:

因为编译器依赖函数序言和结尾来维护局部变量绑定的存储,所以在裸函数中编写除内联汇编之外的任何内容通常是不安全的。LLVM 语言参考将这个特性描述为具有“非常系统特定的后果”,程序员必须意识到这一点。

(强调我的)

在 RFC 稍微低一点的地方,在未解决的问题下,我们了解到这不仅仅是 Rust 的问题。其他语言也遇到此功能的问题:

.. 大多数支持类似功能的编译器都要求或强烈建议作者只在裸函数中编写内联汇编,以确保不会生成假定特定堆栈布局的代码。

原因是所有编译器都对函数的调用方式做了很多假设(关键字:“调用者保存的寄存器”、“被调用者保存的寄存器”、“调用约定”、“红色区域”)。裸函数不遵守这些假设,因此编译器生成的任何代码都有可能是错误的。“解决方案”是不让编译器生成任何东西,即在汇编中手动编写整个函数。

因此,您在裸函数中混合“正常”代码 ( let mut nr: u32 = 0; )、函数调用 ( swi_service_routine(nr);) 和原始汇编程序的方式是未指定行为。(是的,Rust 中存在这样的东西,但仅在 Unstable 中)。

裸函数会引起足够多的问题,以至于它们应该在 Rust 错误跟踪器中贴上自己的标签。在其中一个 A-naked 问题中,我们发现了这个评论,来自知识渊博的用户 Tari(其中包括 的作者llvm-sys。他解释说:

裸函数中非 asm 代码的实际正确性取决于优化器和代码生成器,通常我们无法保证它会做什么。

也有关于需要unsafe裸函数的讨论,因为它们打破了 Rust 的许多正常假设。他们在所有情况下都不需要这个的事实是一个开放的错误


因此,“优化问题”的正确解决方案是完全停止依赖优化。相反,只写一个asm!()块。

对于您的Cpu::save_context()/Cpu::restore_context_and_return()对:我可以理解对代码重用的渴望。要获得它,请将它们更改为插入相关asm!(...). 的串联asm!(...); asm!(...); asm!(...); 应该等同于单个asm!().

于 2017-05-24T15:16:18.233 回答
-5

如果你使用 cargo,你可以告诉它根本不优化任何东西,或者按级别

货物优化

于 2017-05-23T15:37:03.097 回答