调试LLVM IR

时间:2013-05-28 18:19:26

标签: llvm

我已经构建了一个LLVM目标前端,它可以产生一些IR。随后并且完全预期,在某些情况下IR输出是不正确的(因为,它看起来是正确的,但是在执行时结果程序崩溃)。但是,我还没有找到很多有用的工具来解决这个问题。

我尝试过使用lli,但错误信息输出非常无用(当你假设一个解释器可以提供非常精确的错误细节时)。

我研究了将IR转换为C代码,然后使用Visual Studio进行调试,但似乎从LLVM中删除了此功能。

我也考虑过处理GDB。但是,DWARF调试信息格式对于它看起来的几种现有语言来说非常具体,此外,我正在使用我的前端进行翻译的源是正确的,它是生成的IR,这是错误的,因此原始源的调试符号不会没有用 - 例如,我需要查看一堆中间寄存器值的值,这些值与任何源变量或编译器生成的函数中的断点都不对应。

调试LLVM IR输出有哪些工具和技术?

2 个答案:

答案 0 :(得分:4)

我不确定我完全理解你的问题。你是说你的编译器(从语言X到LLVM IR)产生不正确的输出(错误的LLVM IR),你不确定如何调试它?换句话说,有两种可能性:

  1. 您的编译器生成的IR不正确 - 您可以指出某些指令并说 - 这不是我想要生成的。
  2. IR似乎是正确的,但没有产生我期望它产生的结果。
  3. 我认为你(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本身会很棒!

不幸的是,似乎代码不可用。