LLVM pass:遍历模块函数列表时出错

LLVM pass: Error when iterating over Module functions list

我正在尝试在 LLVM 传递中使用 llvm::Module::getFunctionList() 返回的列表迭代模块函数列表。我使用这样的循环:

    for (auto curFref = M->getFunctionList().begin(), 
              endFref = M->getFunctionList().end(); 
              curFref != endFref; ++curFref) {
        errs() << "found function: " << curFref->getName() << "\n";
    }

这个循环的第一次迭代检索了一个函数,正如预期的那样,但它没有检测到列表的末尾并且继续只是在后续迭代中获取不是函数的其他对象(如他们的报告getName()), 比如那个函数参数。几次迭代后,它可能会遇到一些垃圾(或 NULL)并在引用当前 "function" 引用时崩溃。 例如,对于这个程序:

int foo(int k) {
    int i, s = 0;
    for (i = 0; i < k; ++i)
        s += i;
    return s;
}

变成这个IR码:

...
; Function Attrs: nounwind uwtable
define i32 @foo(i32 %k) #0 {
entry:
...

输出将如下所示:

found function: foo
found function: k
found function: #0 0x00007f481f77c46e llvm::sys::PrintStackTrace(llvm::raw_ostream&) /home/me/work/llvm-3.8.0/lib/Support/Unix/Signals.inc:322:0
...

因此您可以看到,在正确迭代 foo 之后,它继续到参数 k 等对象。

我在模块传递(在 runOnModule() 中)和函数传递(使用 F.getParent() 查询包含的模块)中都尝试了这个,并得到了相同的结果.

问题也在 LLVM 3.8.0 和 LLVM 3.5.2 上重复出现。

知道我错过了什么,因为我未能正确迭代返回的函数列表吗?

=====

编辑:

请注意,在模块的函数上使用替代迭代时会显示相同的行为,例如使用 M.begin()/end() 作为迭代器时,甚至使用 C++11 基于范围的 for循环:for (Function &curF: M) ...

此外,M.getFunctionList().size() 在尝试遍历列表项时会导致分段错误。所以看起来函数列表确实损坏了。但这是我在 runOnModule() 入口点开始时得到的列表。所以它看起来不像是我的代码破坏的东西。

======

编辑 2:

我不知道这是否重要,但我的 LLVM pass 是作为动态可加载库从 LLVM 源代码树外部构建的,然后使用 -load=foo.so 命令行选项加载到 opt 中。

您是否尝试过使用 M->begin()M->end() 而不是 M->getFunctionList().begin()M->getFunctionList().end()?这对我有用。

您的输入是否有可能以某种方式损坏?首先尝试 运行ning llvm::verifyModule(来自 Verifier.h),看看你得到什么 - 或者 运行 opt -verify,应该是一样的。此外,您可能对 opt.

-verify-each 命令行选项感兴趣

如果您使用以下方式注册您的通行证:

 char SkeletonPass::ID = 0;

// Automatically enable the pass.
// This pass is taken from Adrian Samson Template project http://adriansampson.net/blog/clangpass.html
static void registerSkeletonPass(const PassManagerBuilder &,
                         legacy::PassManagerBase &PM) {
  PM.add(new SkeletonPass());
}
static RegisterStandardPasses
  RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible,
                 registerSkeletonPass);

这仅适用于 FunctionPass/BasicBlockPass,一种解决方案是照常使用规范传递,不使用任何技巧,如下所示:-

char SkeletonPass::ID = 0;
static RegisterPass<SkeletonPass> X("passname","Pass Name Analysis");
static void registerPass(const PassManagerBuilder &,
                         legacy::PassManagerBase &PM) {
    PM.add(new SkeletonPass());
}
static RegisterStandardPasses
        RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible,
                       registerPass);

并且 运行 您使用以下命令通过:

$ clang -c -O1 -emit-llvm  programs/hello.cpp   -o programs/hello.bc
$ opt   -load build/Debug/libCallgraph.so -passname programs/hello.bc

我是核心 LLVM 开发人员之一。

这几乎可以肯定是 LLVM 的错误检查、错误编译的 LLVM,或者您的通行证是作为单独的 DSO 构建的(尽管我不明白这是怎么回事)。

LLVM 的许多部分使用 M->functions()(或 M->begin()M->end())来仅迭代函数。如果行为被破坏,这些部分将持续失败。这些机制与像 M->getFunctionList() 那样直接访问函数列表之间的唯一区别是列表是否可变。对于您展示的用例,它们应该完全相同的行为。

我建议尝试 运行 LLVM 测试套件以确保其正常工作。如果您使用 Ninja CMake 生成器(如我所推荐的那样)构建 LLVM:

% ninja check-llvm

如果失败,特别是如果它在类似区域崩溃,这似乎是一个非常好的迹象,表明问题出在您的 LLVM 副本中。

无论如何,SO 并不是获得进一步帮助的好地方。问题的答案是 "that should in fact work" 并且我检查了 LLVM 中的代码,但没有看到任何明显的解释。所以我的建议是尝试上面的方法,尝试 LLVM 的新副本(可能是树顶,或最新版本)。确保您拥有足够现代的主机工具链来构建 LLVM。有关详细信息,请参阅本节: http://llvm.org/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library

如果一切都失败了,请尝试联系邮件列表中的开发人员: http://lists.llvm.org/mailman/listinfo/llvm-dev

或尝试 IRC 频道:http://llvm.org/docs/#irc

希望对您有所帮助!