我已经构建了一个LLVM目标前端,它可以产生一些IR。随后并且完全预期,在某些情况下IR输出是不正确的(因为,它看起来是正确的,但是在执行时结果程序崩溃)。但是,我还没有找到很多有用的工具来解决这个问题。
我尝试过使用lli,但错误信息输出非常无用(当你假设一个解释器可以提供非常精确的错误细节时)。
我研究了将IR转换为C代码,然后使用Visual Studio进行调试,但似乎从LLVM中删除了此功能。
我也考虑过处理GDB。但是,DWARF调试信息格式对于它看起来的几种现有语言来说非常具体,此外,我正在使用我的前端进行翻译的源是正确的,它是生成的IR,这是错误的,因此原始源的调试符号不会没有用 - 例如,我需要查看一堆中间寄存器值的值,这些值与任何源变量或编译器生成的函数中的断点都不对应。
调试LLVM IR输出有哪些工具和技术?
答案 0 :(得分:4)
我不确定我完全理解你的问题。你是说你的编译器(从语言X到LLVM IR)产生不正确的输出(错误的LLVM IR),你不确定如何调试它?换句话说,有两种可能性:
我认为你(1)正在谈论(因为这是问题所说的,在你更新之前)
然后,这不是LLVM特有的问题。假设您正在将编译器从语言X编写为本机代码。生成的本机代码不正确 - 您如何调试问题?好吧,显然你调试你的编译器。您试图找到编译器对输入的理解是正确的最后位置,或者是第一个不正确的位置。如何做到这一点在很大程度上取决于编译器的体系结构。但是,有一些帮助很多的东西是编译器中其他中间层的可打印表示。
例如,Clang(从C,C ++和Objective C生成LLVM IR)可以转储其完整的AST。因此,查看AST的错误代码可以将编译器减少一半,从而帮助确定问题是在前端(C源 - > AST)还是代码生成(AST - > LLVM IR)。 LLVM后端(将LLVM IR编译为本机代码)也有一些中间层(最值得注意的是SelectionDAG和MI),可以为调试进行检查。这些只是其他现有编译器的例子,YMMV和你的。
答案 1 :(得分:2)
Will Diez描述了他是如何实现的:
https://groups.google.com/d/msg/llvm-dev/O4Dj9FW1gtM/ovnm6dqoJJsJ
大家好,
出于我自己的目的,我写了一个完全符合你们所有人的通行证 描述:将调试元数据添加到LLVM IR。
作为一个通行证,它必须解决“这个文件需要存在的问题 在某个地方的磁盘上所以gdb可以找到它“,我解决了我的问题 到/ tmp /某处。不是一个很好的解决方案(谁删除这些?)但是 工作得很好。
另一个有趣的问题是如何与任何现有的调试共存 元数据,可用于同时调试IR 使用C源进行内联转换以进行检测式传递 像SAFECode,ASan / TSan。
快速示例:
(gdb) break main Breakpoint 1 at 0x4010b1: file /home/wdietz2/magic/test/unit/test_loop.c, line 9. (gdb) r Starting program: /home/wdietz2/llvm/32-obj-make/projects/magic/test/Output/test_loop Breakpoint 1, main (argc=<value optimized out>, argv=<value optimized out>) at /home/wdietz2/magic/test/unit/test_loop.c:9 9 unsigned k = 0; Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.5.x86_64 libgcc-4.4.6-4.el6.x86_64 libstdc++-4.4.6-4.el6.x86_64 (gdb) n 10 source(argc != 0, &k); (gdb) n 14 %and.i.i.i.i104 = and i64 %4, 70368744177660 (gdb) n 15 %5 = load i8** @global, align 8 (gdb) n 18 store i32 16843009, i32* %6, align 1 (gdb) n 19 store i8 1, i8* getelementptr inbounds ([1 x i8]* @array, i64 0, i64 0), align 1 (gdb) n 20 call coldcc void @runtime_func() nounwind (gdb) n 11 while(i-- > argc) (gdb) n 23 %and.i.i.i.i85 = and i64 %7, 70368744177660 (gdb) n 14 while(j++ < i) k += j; (gdb) n 11 while(i-- > argc) (gdb) n 14 while(j++ < i) k += j; (gdb) n 102 %77 = load i8** @global, align 8 (gdb) n 105 %79 = load i32* %78, align 4 (gdb) n 106 %cmp7.i.i.i = icmp ne i32 %79, 0 (gdb) n 108 call void @llvm.memset.p0i8.i64(i8* %add.ptr.i.i.i.i86, i8 %conv8.i.i.i, i64 4, i32 1, i1 false) nounwind (gdb) n 14 while(j++ < i) k += j; (gdb) n 15 while(j-- > 0) k *= k + j; (gdb) n 95 %69 = load i8** @global, align 8 (gdb) n 98 %71 = load i32* %70, align 4 (gdb)
传球本身很简单 - 它解决的难题是 将IR发送到磁盘并推理出什么指令* 什么线,如果做得好,真的不应该是一个问题 LLVM。如果需要,我当然可以根据要求提供代码。
简而言之,它对我来说似乎运作良好并且正确地完成了它 LLVM本身会很棒!
不幸的是,似乎代码不可用。