如何提交 LLVM 错误报告

简介 - 发现错误?

如果您在使用 LLVM 时遇到了错误,我们非常希望了解它。本文档描述了您可以做些什么来增加快速修复它的几率。

🔒 如果您认为该错误与安全相关,请遵循如何报告安全问题?。 🔒

基本上,您至少需要做两件事。首先,确定错误是导致编译器崩溃还是编译器错误编译了程序(即,编译器成功生成了可执行文件,但它运行不正常)。根据错误的类型,按照链接部分中的说明缩小错误的范围,以便修复它的人能够更容易地找到问题。

一旦您有了一个缩减后的测试用例,请转到LLVM 错误跟踪系统并填写包含必要详细信息的表格(请注意,您不需要选择标签,如果不确定就使用)。错误描述应包含以下信息

  • 重现问题所需的所有信息。

  • 触发错误的缩减后的测试用例。

  • 您获取 LLVM 的位置(如果不是从我们的 Git 仓库)。

感谢您帮助我们改进 LLVM!

崩溃错误

通常,编译器中的错误会导致它崩溃——通常是由于某种断言失败。最重要的一点是弄清楚它是在 Clang 前端崩溃,还是 LLVM 库之一(例如,优化器或代码生成器)有问题。

要确定哪个组件崩溃(前端、中端优化器或后端代码生成器),请像崩溃时一样运行 clang 命令行,但使用以下额外的命令行选项

  • -emit-llvm -Xclang -disable-llvm-passes:如果 clang 在传递这些选项(禁用优化器和代码生成器)时仍然崩溃,则崩溃发生在前端。跳到前端错误

  • -emit-llvm:如果 clang 在使用此选项(禁用代码生成器)时崩溃,则您发现了中端优化器错误。跳到中端错误

  • 否则,您遇到了后端代码生成器崩溃。跳到代码生成器错误

前端错误

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

creduce 工具可以帮助将预处理文件缩减到仍然重现问题的最小代码量。我们鼓励您使用 creduce 来缩减代码,以使开发人员的生活更轻松。clang/utils/creduce-clang-crash.py 脚本可用于 clang 转储的文件,以帮助自动化创建测试来检查编译器崩溃。

cvisecreduce 的替代方案。

中端优化错误

如果您发现错误在优化器中崩溃,请通过传递“-emit-llvm -O1 -Xclang -disable-llvm-passes -c -o foo.bc”将您的测试用例编译为 .bc 文件。-O1 很重要,因为 -O0 会将 optnone 函数属性添加到所有函数,并且许多 pass 不会在 optnone 函数上运行。然后运行

opt -O3 foo.bc -disable-output

如果这没有崩溃,请按照前端错误的说明进行操作。

如果这确实崩溃了,那么您应该能够使用以下 bugpoint 命令来调试它

bugpoint foo.bc -O3

运行此命令,然后提交包含 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。请注意,这非常冗长。

后端代码生成器错误

如果您发现错误导致 clang 在代码生成器中崩溃,请通过将“-emit-llvm -c -o foo.bc”传递给 clang(除了您已经传递的选项之外)将您的源文件编译为 .bc 文件。一旦您有了 foo.bc,以下命令之一应该会失败

  1. llc foo.bc

  2. llc foo.bc -relocation-model=pic

  3. llc foo.bc -relocation-model=static

如果这些命令都没有崩溃,请按照前端错误的说明进行操作。如果其中一个命令崩溃了,您应该能够使用以下 bugpoint 命令行之一来缩减它(使用与上面失败的命令对应的命令行)

  1. bugpoint -run-llc foo.bc

  2. bugpoint -run-llc foo.bc --tool-args -relocation-model=pic

  3. bugpoint -run-llc foo.bc --tool-args -relocation-model=static

请运行此命令,然后提交包含 bugpoint 发出的说明和缩减后的 .bc 文件的错误报告。如果 bugpoint 出现问题,请提交“foo.bc”文件以及 llc 崩溃时使用的选项。

LTO 错误

如果您在使用 -flto 选项时遇到导致 LLVM LTO 阶段崩溃的错误,请按照以下步骤诊断和报告问题

除了您现有的编译选项外,还使用以下选项将源文件编译为 .bc (Bitcode) 文件

export CFLAGS="-flto -fuse-ld=lld" CXXFLAGS="-flto -fuse-ld=lld" LDFLAGS="-Wl,-plugin-opt=save-temps"

这些选项启用 LTO 并保存编译期间生成的临时文件,以供以后分析。

在 Windows 上,您应该使用 lld-link 作为链接器。调整您的编译标志如下:* 将 /lldsavetemps 添加到链接器标志。* 当从编译器驱动程序链接时,添加 /link /lldsavetemps 以将该标志转发到链接器。

使用指定的标志将生成四个中间字节码文件

  1. a.out.0.0.preopt.bc(在应用任何链接时优化 (LTO) 之前)

  2. a.out.0.2.internalize.bc(在应用初始优化之后)

  3. a.out.0.4.opt.bc(在一组广泛的优化之后)

  4. a.out.0.5.precodegen.bc(在 LTO 之后但在转换为机器代码之前)

执行以下命令之一来确定问题的来源

  1. opt "-passes=lto<O3>" a.out.0.2.internalize.bc

  2. 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

这里我们 grepped 了失败的断言消息。

请运行此命令,然后提交包含 llvm-reduce 发出的说明和缩减后的 .bc 文件的错误报告。

错误编译

如果 clang 成功生成了可执行文件,但该可执行文件运行不正常,那么这要么是代码中的错误,要么是编译器中的错误。首先要检查的是确保它没有使用未定义的行为(例如,在定义变量之前读取它)。特别是,检查程序在各种sanitizers(例如 clang -fsanitize=undefined,address)和 valgrind 下是否干净。我们追踪到的许多“LLVM 错误”最终都是被编译程序中的错误,而不是 LLVM。

一旦您确定程序本身没有错误,您应该选择要使用哪个代码生成器来编译程序(例如 LLC 或 JIT),并可选择一系列要运行的 LLVM pass。例如

bugpoint -run-llc [... optzn passes ...] file-to-test.bc --args -- [program arguments]

bugpoint 将尝试缩小您的 pass 列表,以找到导致错误的 pass,并尽可能简化 bitcode 文件以帮助您。它将打印一条消息,告知您如何重现最终的错误。

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 找到的错误,只需执行以下操作

  1. 从安全 bitcode 文件重新生成共享对象

    llc -march=c safe.bc -o safe.c
    gcc -shared safe.c -o safe.so
    
  2. 如果调试 LLC,编译测试 bitcode 本机代码并与共享对象链接

    llc test.bc -o test.s
    gcc test.s safe.so -o test.llc
    ./test.llc [program options]
    
  3. 如果调试 JIT,加载共享对象并提供测试 bitcode

    lli -load=safe.so test.bc [program options]