不,你不能。
Rust的编译单元(编译器和优化器操作的最小单元)是整个crate。
你唯一的解决方法是在单独的crate中编译此函数,将其编译,然后将其作为预编译依赖项包含进来。(普通的rust依赖项在依赖者的优化级别下编译)
然而:为这个单独的函数指定不同的优化级别并不能解决你的问题!当然,它可能今天可以工作,但每次编译器(或优化标志)更改时都可能会再次出现问题。鉴于你示例中的函数名称(Cpu::save_context/restore_context_and_return),你似乎正在解决的根本问题需要向rustc添加适当的调用约定。
简而言之:裸函数非常不安全(我很尊敬你,你比我勇敢!)。唯一可靠的使用方式是编写一个完整的函数体,只有一个asm!()块,没有其他内容。
混合使用
asm!
、普通的Rust和函数调用,实际上是未定义行为(在可怕的C/Nasal-Demon意义上)。
无论如何优化调整都不会改变这一点。
2022年4月更新:自原来回答此问题以来,裸函数方面发生了很多事情。
裸函数的最小“约束”子集(参见RFC#2972)
1即将稳定下来。
还有编译器错误会
"拒绝不支持的裸函数",这些错误会触发此处提供的示例。
1.60版本
赤裸函数在 Rust 作者 "搞定" 它之前仍然不稳定。正如您所发现的,这存在许多微妙的问题。
稳定问题追踪
在这里,已于2022年被更为有限的
"受限制"赤裸函数追踪器所取代。
在naked-fn RFC中,“动机”部分中,我们可以找到:
由于编译器依赖于函数序言和尾声来维护本地变量绑定的存储,因此除内联汇编外,写入任何内容都是不安全的。 LLVM 语言参考将此功能描述为具有“非常系统特定的后果”,程序员必须意识到这一点。
(重点是我的)
在RFC的稍低部分,
未解决的问题下面,我们得知这不仅是Rust的问题。其他编程语言也会遇到此功能的问题:
“..大多数支持类似功能的编译器要求或强烈建议作者
只在裸函数内写入内联汇编,以确保生成的代码不会假定特定的堆栈布局。”
原因在于所有编译器都对函数调用有很多假设(关键词:“Caller-Saved Registers”,“Callee-saved registers”,“Calling convention”,“Red zone”)。裸函数不遵守这些假设,因此编译器生成的任何代码都
极有可能是错误的。 “解决方案”是不让编译器生成任何东西,即手动用汇编语言编写整个函数。
作为这样的,你将'正常'代码(
let mut nr: u32 = 0;
)、函数调用(
swi_service_routine(nr);
)和裸汇编混合在一个裸函数中是
未指定的行为。(是的,在Rust中存在这样的事情,但只在不稳定版本中存在)。
裸函数引起了足够多的问题,以至于它们在Rust bugtracker中应该有
自己的标签。在其中一个A-naked问题中,我们发现
这个评论,由知识渊博的用户Tari(作者之一
llvm-sys
)解释道:
非汇编代码在裸函数中的实际正确性取决于优化器和代码生成器,一般来说我们无法保证它会做什么。
有关裸函数,也有讨论要求使用unsafe
,因为它们破坏了Rust的许多正常假设。 事实上,在所有情况下它们尚未要求此是一个未解决的错误
2022更新: 通过新的默认拒绝lints于2022-01-21关闭,以拒绝不支持的裸函数(#93153)。
所以,你的“优化问题”的正确解决方案是停止依赖优化。相反,只编写一个单一的
asm!()
块。
对于你的
Cpu::save_context()
/
Cpu::restore_context_and_return()
配对:我可以理解对代码重用的渴望。为了实现它,将其更改为插入相关
asm!(...)
的宏即可。
asm!(...); asm!(...); asm!(...);
的连接应该等同于单个
asm!()
。