如何在Clang中启用内联函数的编译?

4

我正在使用Clang作为库来生成一些LLVM IR模块。

这是该模块的源代码:

inline int getSevenInline() { 
  return 7; 
}

int getSeven() { 
  return getSevenInline(); 
}

我期望LLVM IR模块包含一个函数getSeven,它返回7

这是我的程序生成的LLVM IR:

; ModuleID = './test.cpp'
source_filename = "./test.cpp"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"

; Function Attrs: noinline ssp uwtable
define i32 @_Z8getSevenv() #0 {
entry:
  %call = call i32 @_Z14getSevenInlinev()
  ret i32 %call
}

declare i32 @_Z14getSevenInlinev() #1

attributes #0 = { noinline ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

当我尝试执行该模块时,它无法解析getSevenInline符号。 IR似乎有两个错误:
  1. 函数getSevenInline不应该存在,因为它应该被内联。
  2. 虽然未被内联,但getSevenInline没有实现。
我应该在我的clang::CompilerInstance上配置什么才能正确编译inline函数呢? 我只遇到了inline函数的问题;非inline函数正常工作。
很抱歉,我有太多代码要发布生成IR的整个程序,但我希望有人可以指向Clang源代码中的此配置。

3
据我所知,编译器是否将函数内联与关键字“inline”的存在几乎没有什么关系。 - 463035818_is_not_a_number
2个回答

1
C++规范赋予编译器广泛的自由裁量权,决定何时内联函数或不内联函数。即使您明确将函数声明为内联,对于编译器而言,它仍然只是一个建议,如果编译器认为生成的机器代码过于臃肿或效率低下,则可以自由忽略该建议。这还严重取决于您传递给编译器的优化标志以及许多其他完全由编译器实现者自行决定的实现相关细节。 C++ FAQ提供了有关此主题的更多详细信息:
“有几种方法可以指定函数为内联函数,其中一些涉及inline关键字,而其他一些则不涉及。无论如何指定函数为内联函数,都是请求编译器可以忽略的:编译器可能会在调用被指定为内联的函数的某些、所有或没有地方进行内联扩展。(如果这似乎毫无意义,请不要灰心。上述灵活性实际上是一个巨大的优势:它允许编译器将大型函数与小型函数区别对待,并且如果选择正确的编译器选项,它还允许编译器生成易于调试的代码。)”

inline 关键字的作用是确保同名函数不会出现多次定义错误。例如,如果你有以下代码(在 myinlines.h 文件中):

inline int add(int a, int b)
{
    return a + b;
}

如果你在file1.cpp和file2.cpp中都包含myinlines.h文件,那么当你试图将file1.o和file2.o链接成最终可执行文件时,即使它们都包含int add(int,int)的定义,你也不会得到链接错误。CPPReference有更多细节说明:只要每个定义出现在不同的翻译单元中,并且(对于非静态内联函数)所有定义都相同,程序中可能存在一个内联函数的多个定义。例如,可以在多个源文件中#include的头文件中定义内联函数。

谢谢提供的背景信息,但这并没有真正回答我的问题。Clang应该要么实际内联函数,要么不内联它,而是添加一个实现。在我的代码中,它既没有内联也没有生成实现。 - sdgfsdh
1
@sdgfsdh,说得好。我不是Clang专家,所以我会让其他人来处理它。 - Chris Vig

1

我设法让它工作了,但我不会假装自己知道它为什么能工作:

for (auto declGroup : declGroups) {
  codeGenerator->HandleTopLevelDecl(declGroup);
}

// For some reason this triggers the code generation for inline functions
codeGenerator->HandleTranslationUnit(compilerInstance.getASTContext());

我认为这与延迟声明有关;HandleTranslationUnit告诉CodeGenerator应该完成编译。

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