LLVM pass:迭代模块函数列表时出错
Posted
技术标签:
【中文标题】LLVM pass:迭代模块函数列表时出错【英文标题】:LLVM pass: Error when iterating over Module functions list 【发布时间】:2016-03-30 12:29:08 【问题描述】:我正在尝试在 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)并在引用当前“函数”引用时崩溃。
例如,对于这个程序:
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 通行证是从 LLVM 源树外部构建为动态可加载库,然后使用 -load=foo.so
命令行选项加载到 opt
中。
【问题讨论】:
@Chandler Carruth 的回答解决了这个问题。在他回答的核心中,当我使用 make/configure 构建 LLVM 时,他建议使用 ninja/cmake 构建 LLVM。使用 ninja/cmake 重建消除了这个问题。虽然 LLVM 3.8.0 发行说明表明仍然支持使用 make/configure 进行构建,但这个问题表明它以某种方式被破坏了。 【参考方案1】:我是核心 LLVM 开发人员之一。
这几乎可以肯定是对 LLVM 的错误检出、错误编译的 LLVM,或者您的通行证是作为单独的 DSO 构建的问题(尽管我不明白这是怎么回事)。
LLVM 的许多部分使用M->functions()
(或M->begin()
和M->end()
)仅迭代函数。如果行为被破坏,这些部分将始终失败。这些机制与像M->getFunctionList()
那样直接访问函数列表之间的唯一区别是列表是否是可变的。对于您展示的用例,它们应该完全具有相同的行为。
我建议尝试运行 LLVM 测试套件以确保它正常工作。如果你使用 Ninja CMake 生成器(我会推荐)来构建 LLVM:
% ninja check-llvm
如果这失败了,特别是如果它在类似区域崩溃,这似乎是一个非常好的迹象,表明问题出在你的 LLVM 副本中。
无论如何,SO 并不是在这里获得进一步帮助的好地方。问题的答案是“实际上应该可以工作”,我检查了 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
希望这会有所帮助!
【讨论】:
我毫不怀疑这个迭代器适用于 LLVM 项目中构建的模块。但在我的情况下,我将模块传递构建为共享对象,并使用“opt”的“-load”选项加载它。所以我怀疑这个用例可能有问题(即,当这个用例被破坏时,LLVM 中的所有东西都可以工作)。我还使用自己的 makefile 在外部构建 pass 共享对象,依赖于llvm-config --cppflags
提供的编译标志。因此,llvm-config --cppflags
输出可能与用于内部构建的标志不同。你知道这些用例是否经过测试?
看来您建议使用 ninja 进行构建具有“魔力”。我最初的 LLVM 构建使用了 configure/make。虽然官方在 3.8.0 中仍然支持它,但它似乎有问题。使用 ninja 重建 LLVM 并针对更新的(ninja)LLVM 使用相同的 makefile 构建我的 Module pass 解决了这个问题。我想 LLVM 的基于 make 的构建并不受支持,尽管发行说明表明它将仅从 3.9 停止支持。谢谢。【参考方案2】:
您是否尝试过使用M->begin()
和M->end()
而不是M->getFunctionList().begin()
和M->getFunctionList().end()
?这对我有用。
【讨论】:
M->begin()/end()
只是 M->getFunctionsList().begin()/end()
的有限版本。无论如何,我也尝试了简单的迭代器,得到了同样不幸的结果。【参考方案3】:
您的输入是否可能以某种方式损坏?尝试先在上面运行llvm::verifyModule
(来自Verifier.h),看看你得到了什么——或者在上面运行opt -verify
,应该是一样的。此外,您可能对 opt 的 -verify-each
命令行选项感兴趣。
【讨论】:
在我测试的任何模块上都会重现该问题。我所有的输入模块都是使用以下命令从 C 代码生成的:clang -c -emit-llvm -o foo-preopt.bc foo.c
,然后是 opt -mem2reg -licm -S -o foo.ll foo-preopt.bc
。因此,如果任何模块损坏,它是clang
或标准opt
传递的结果。无论如何,-verify
没有报告任何问题。我使用 LLVM 的稳定版本。
@ElazarR 另一个想法:确保您使用的是与 opt 一起构建的 clang,而不是操作系统提供的 clang。
我使用的 LLVM 工具是一致的。实际上,我避免在我的操作系统中安装 clang/LLVM 包。我只使用从相应版本源代码构建的 clang/LLVM,并提供每个工具的显式路径。【参考方案4】:
如果您使用以下方式注册您的通行证:
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);
然后使用以下 cmd 运行您的通行证:
$ clang -c -O1 -emit-llvm programs/hello.cpp -o programs/hello.bc
$ opt -load build/Debug/libCallgraph.so -passname programs/hello.bc
【讨论】:
很遗憾,这并不能解决问题。迭代 M.getFunctionList() 仍然返回以模块中的第一个函数开头并以非函数符号(对象)继续的项目,例如该函数的参数(正如我在问题中报告的那样)。实际上,isa以上是关于LLVM pass:迭代模块函数列表时出错的主要内容,如果未能解决你的问题,请参考以下文章