为什么标记为`#[inline]`的pub函数不会出现在编译后的目标文件中?

4

我使用 cargo new a 创建了一个全新的板条箱,并在 src/lib.rs 中输入了以下内容:

pub fn xor(a: i32, b: i32) -> i32 {
    a ^ b
}

#[inline]
pub fn xor_inline(a: i32, b: i32) -> i32 {
    a ^ b
}

当我使用cargo build --release进行编译时,生成的.rlib只包含xor而不包括xor_inline

$ gnm -D -C target/release/deps/liba-6a3c2798185fafee.rlib
gnm: __.SYMDEF: File format not recognized

a-6a3c2798185fafee.0.o:
0000000000000000 T a::xor::hf0d97103d53d3286
gnm: rust.metadata.bin: File format not recognized
gnm: a-6a3c2798185fafee.0.bytecode.deflate: File format not recognized

(gnm是通过Homebrew在MacOS上安装的GNU nm。)

我有两个问题:

  1. 为什么xor_inline不在目标文件中?我认为它的源代码必须存在于rust.metadata.bin中,以便跨crate内联起作用,但为什么普通函数没有从目标文件中导出?

  2. 是否有任何rustc标志可以与cargo rustc --release -- ...一起使用,以确保所有#[inline]函数都在目标文件中?(也许是不同的--crate-type-C下的标志之一?)

(我需要这样做是因为我想检查我的crate中函数生成的汇编代码,而不必删除所有内联属性或将每个内联函数包装在公共非内联函数中。)


相关:根据这个答案,rlib保留了一个标记为内联的函数列表。 - E net4
1
为什么纯函数没有从目标文件中导出?我认为这段代码足够简单,总是被内联,因此永远不会被调用,所以没有必要有目标代码。我想检查我的箱中函数生成的汇编代码。一旦内联,该汇编代码可能看起来完全不像内联和优化后的汇编代码,因此在内联之前检查它甚至可能没有用。 - Shepmaster
1个回答

6
在Rust 1.13.0之前,对于#[inline]函数实际上总是生成本地代码。这在Rust 1.13.0中发生了变化:未在crate中使用的#[inline]函数不会被编译成本地代码。这个主要动机是,如果库中包含许多自己不使用的内联函数,则如果它们根本不被翻译为本地代码,则编译速度会更快。
编译器仍然以中间表示形式输出该函数,以便编译器可以在其他crate中进行内联和优化。

(我需要这个功能,因为我想检查我的crate中函数的生成汇编,而不必删除所有内联属性或将每个内联函数包装在公共非内联函数中。)

内联函数的整个意义在于允许编译器根据函数的使用方式发出不同的汇编代码。例如,接收参数的内联函数可能会使它们的参数参与常量折叠,如果某些参数是常数。有时,内联函数调用可以编译为零条指令(当Rust声称零成本抽象时,它们确实是这样!),但您无法从非内联函数调用中得知这一点!因此,我怀疑您想要做的事情只会误导您;当使用您的内联函数时,您将无法看到编译器实际发出的代码。

有道理,谢谢!我想查看函数的汇编代码是为了确保一些特定的边界检查已经被消除。我猜想如果它们在没有内联的情况下被消除了,那么在内联的情况下也会被消除(但反过来不一定成立,因为周围的代码可能会帮助编译器消除更多的检查)。有没有更好的方法可以查看函数的独立汇编,而不是为每个函数创建一个公共的非内联包装器? - Dogbert
@Dogbert: 我猜如果它们没有被内联消除,它们也会被内联消除(但反过来未必成立,因为周围的代码可能会帮助编译器消除更多的检查)。似乎合理的事情并不总是成立;我在想优化应用的顺序是否可以通过在函数内联时先进行先前的优化来防止边界检查的省略,而这些先前的优化会在函数内联时混淆视听 :/ - Matthieu M.

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