如何提交 LLVM 错误报告¶
简介 - 发现 Bug 了?¶
如果您在使用 LLVM 时遇到 Bug,我们非常希望了解情况。本文档介绍了您可以采取哪些措施来提高快速修复 Bug 的几率。
🔒 如果您认为该 Bug 与安全相关,请遵循 如何报告安全问题?。🔒
基本上,您至少需要做两件事。首先,确定该 Bug 是 导致编译器崩溃 还是编译器 编译错误(即,编译器成功生成可执行文件,但运行不正确)。根据 Bug 的类型,请按照链接部分中的说明缩小 Bug 范围,以便修复人员能够更容易地找到问题。
一旦您得到了一个简化的测试用例,请访问 LLVM Bug 追踪系统 并填写必要的详细信息(请注意,您不需要选择标签,如果不确定,请跳过)。Bug 描述应包含以下信息
重现问题所需的所有信息。
触发 Bug 的简化测试用例。
您获取 LLVM 的位置(如果不是从我们的 Git 代码库获取)。
感谢您帮助我们改进 LLVM!
崩溃 Bug¶
通常情况下,编译器中的 Bug 会导致其崩溃,通常是由于某种断言失败。最关键的一步是确定崩溃发生在 Clang 前端还是 LLVM 库(例如优化器或代码生成器)中。
为了确定哪个组件发生了崩溃(前端、中间端优化器或后端代码生成器),请在崩溃发生时使用以下额外的命令行选项运行 clang
命令
-emit-llvm -Xclang -disable-llvm-passes
:如果使用这些选项(禁用优化器和代码生成器)后clang
仍然崩溃,则崩溃发生在前端。跳至 前端 Bug。-emit-llvm
:如果使用此选项(禁用代码生成器)后clang
崩溃,则您发现了中间端优化器 Bug。跳至 中间端 Bug。否则,您遇到了后端代码生成器崩溃。跳至 代码生成器 Bug。
前端 Bug¶
在 clang
崩溃时,编译器会转储一个预处理文件和一个重现 clang
命令的脚本。例如,您应该会看到类似以下内容
PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/foo-xxxxxx.c
clang: note: diagnostic msg: /tmp/foo-xxxxxx.sh
The creduce 工具有助于将预处理文件缩减到仍然可以复制问题的最小代码量。鼓励您使用 creduce 来缩减代码,从而简化开发人员的工作。The clang/utils/creduce-clang-crash.py
脚本可用于 clang 转储的文件,以帮助自动创建用于检查编译器崩溃的测试。
cvise 是 creduce
的替代方案。
中间端优化 Bug¶
如果您发现某个 Bug 导致优化器崩溃,请通过传递“-emit-llvm -O1 -Xclang -disable-llvm-passes -c -o foo.bc
”将您的测试用例编译为 .bc
文件。The -O1
非常重要,因为 -O0
会将 optnone
函数属性添加到所有函数,并且许多 Pass 不会在 optnone
函数上运行。然后运行
opt -O3 foo.bc -disable-output
如果这没有导致崩溃,请按照 前端 Bug 的说明进行操作。
如果这确实导致了崩溃,那么您应该可以使用以下 bugpoint 命令进行调试
bugpoint foo.bc -O3
运行此命令,然后提交一个 Bug,其中包含 bugpoint 生成的说明和简化的 .bc 文件。
如果 bugpoint 没有重现崩溃,llvm-reduce
是一种简化 LLVM IR 的替代方法。创建一个重现崩溃的脚本并运行
llvm-reduce --test=path/to/script foo.bc
这应该会生成简化的 IR 来重现崩溃。需要注意的是,llvm-reduce
仍然相当不成熟,可能会崩溃。
如果以上方法都不起作用,您可以通过使用 --print-before-all --print-module-scope
标志运行 opt
命令来获取崩溃前的 IR,以在每个 Pass 之前转储 IR。需要注意的是,这会产生非常详细的输出。
后端代码生成器 Bug¶
如果您发现某个 Bug 导致 clang 在代码生成器中崩溃,请通过将“-emit-llvm -c -o foo.bc
”传递给 clang(以及您已经传递的选项)来将源文件编译为 .bc 文件。一旦您拥有 foo.bc,以下命令之一应该会失败
llc foo.bc
llc foo.bc -relocation-model=pic
llc foo.bc -relocation-model=static
如果这些命令都没有导致崩溃,请按照 前端 Bug 的说明进行操作。如果其中一个命令导致了崩溃,您应该可以使用以下 bugpoint 命令行之一进行简化(使用上面导致失败的命令对应的命令行)
bugpoint -run-llc foo.bc
bugpoint -run-llc foo.bc --tool-args -relocation-model=pic
bugpoint -run-llc foo.bc --tool-args -relocation-model=static
请运行此命令,然后提交一个 Bug,其中包含 bugpoint 生成的说明和简化的 .bc 文件。如果 bugpoint 出现问题,请提交“foo.bc”文件和 llc 崩溃的选项。
LTO Bug¶
如果您遇到在使用 -flto
选项时导致 LLVM LTO 阶段崩溃的 Bug,请按照以下步骤诊断和报告问题
使用以下选项(以及您现有的编译选项)将源文件编译为 .bc
(位码)文件
export CFLAGS="-flto -fuse-ld=lld" CXXFLAGS="-flto -fuse-ld=lld" LDFLAGS="-Wl,-plugin-opt=save-temps"
这些选项启用 LTO 并保存编译期间生成的临时文件,以便以后分析。
在 Windows 上,您应该使用 lld-link 作为链接器。请调整您的编译标志如下:* 将 /lldsavetemps
添加到链接器标志中。* 当从编译器驱动程序链接时,添加 /link /lldsavetemps
以便将该标志转发到链接器。
使用指定的标志将生成四个中间字节码文件
a.out.0.0.preopt.bc(在应用任何链接时优化(LTO)之前)
a.out.0.2.internalize.bc(在应用初始优化之后)
a.out.0.4.opt.bc(在进行大量优化之后)
a.out.0.5.precodegen.bc(在 LTO 之后但在转换为机器代码之前)
执行以下命令之一以识别问题根源
opt "-passes=lto<O3>" a.out.0.2.internalize.bc
llc a.out.0.5.precodegen.bc
如果其中一个命令导致了崩溃,您应该可以使用 llvm-reduce 命令行进行简化(使用上面导致失败的命令对应的 bc 文件)
llvm-reduce --test reduce.sh a.out.0.2.internalize.bc
reduce.sh 脚本示例
$ cat reduce.sh
#!/bin/bash -e
path/to/not --crash path/to/opt "-passes=lto<O3>" $1 -o temp.bc 2> err.log
grep -q "It->second == &Insn" err.log
这里我们已经 grep 了失败的断言消息。
请运行此命令,然后提交一个 Bug,其中包含 llvm-reduce 生成的说明和简化的 .bc 文件。
编译错误¶
如果 clang 成功生成可执行文件,但该可执行文件运行不正确,则可能是代码中的 Bug 或编译器中的 Bug。首先要检查的是确保它没有使用未定义的行为(例如在定义变量之前读取变量)。特别是,检查程序在各种 sanitizers(例如 clang -fsanitize=undefined,address
)和 valgrind 下是否干净。我们追查到的许多“LLVM Bug”最终都是被编译程序中的 Bug,而不是 LLVM 本身。
一旦确定程序本身没有 Bug,您应该选择希望使用哪个代码生成器来编译程序(例如 LLC 或 JIT),并可以选择运行一系列 LLVM Pass。例如
bugpoint -run-llc [... optzn passes ...] file-to-test.bc --args -- [program arguments]
bugpoint 会尝试将您的 Pass 列表缩小到导致错误的那个 Pass,并尽可能地简化 bitcode 文件以帮助您。它会打印一条消息,告诉您如何重现结果错误。
The OptBisect 页面显示了一种查找错误优化 Pass 的替代方法。
错误的代码生成¶
类似于通过行为异常的 Pass 调试错误的编译,您可以使用 bugpoint
调试 LLC 或 JIT 生成的错误代码。在这种情况下,bugpoint
的流程是尝试将代码缩小到一个由其中一种方法错误编译的函数,但由于正确性需要运行整个程序,因此 bugpoint
会使用 C 后端编译它认为不受影响的代码,然后链接它生成的共享对象。
调试 JIT
bugpoint -run-jit -output=[correct output file] [bitcode file] \
--tool-args -- [arguments to pass to lli] \
--args -- [program arguments]
类似地,要调试 LLC,可以运行
bugpoint -run-llc -output=[correct output file] [bitcode file] \
--tool-args -- [arguments to pass to llc] \
--args -- [program arguments]
特别说明:如果您正在调试 llvm/test
层次结构中已存在的 MultiSource 或 SPEC 测试,则有一种更简单的调试 JIT、LLC 和 CBE 的方法,即使用预先编写的 Makefile 目标,这些目标将传递 Makefile 中指定的程序选项。
cd llvm/test/../../program
make bugpoint-jit
在 bugpoint
运行成功结束时,您将获得两个 bitcode 文件:一个 *安全* 文件,可以使用 C 后端编译;以及一个 *测试* 文件,该文件由 LLC 或 JIT 错误代码生成,从而导致错误。
要重现 bugpoint
找到的错误,只需执行以下操作即可
从安全 bitcode 文件重新生成共享对象。
llc -march=c safe.bc -o safe.c gcc -shared safe.c -o safe.so
如果调试 LLC,则将测试 bitcode 编译为原生代码,并与共享对象链接。
llc test.bc -o test.s gcc test.s safe.so -o test.llc ./test.llc [program options]
如果调试 JIT,则加载共享对象并提供测试 bitcode。
lli -load=safe.so test.bc [program options]