如何提交 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 函數屬性新增到所有函數,而許多傳遞不會在 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,以在每次傳遞之前傾印 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(位元組碼)檔案

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

在這裡,我們使用了 grep 來搜尋失敗的斷言訊息。

請執行此操作,然後提交錯誤報告,報告中需包含說明和 llvm-reduce 輸出的精簡版 .bc 檔案。

編譯錯誤

如果 clang 成功產生了一個可執行檔,但該可執行檔無法正常執行,則表示程式碼或編譯器中存在錯誤。首先要檢查的是,確保它沒有使用未定義的行為(例如,在定義變數之前讀取變數)。特別是,請檢查程式碼在各種 sanitizers(例如,clang -fsanitize=undefined,address)和 valgrind 下是否乾淨。我們追蹤過的許多「LLVM 錯誤」最終都是被編譯的程式碼中的錯誤,而不是 LLVM 本身的錯誤。

一旦您確定程式碼本身沒有錯誤,您應該選擇要用哪個程式碼產生器來編譯程式碼(例如,LLC 或 JIT),以及選擇性地執行一系列 LLVM 優化過程。例如

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

bugpoint 會嘗試將您的優化過程清單縮小到導致錯誤的那個,並盡可能簡化位元組碼檔案,以協助您。它會印出一條訊息,讓您知道如何重現產生的錯誤。

OptBisect 頁面顯示了另一種尋找錯誤最佳化過程的方法。

程式碼產生錯誤

與除錯行為異常的 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 測試,則可以使用預先寫好的 Makefile 目標,以更簡單的方式除錯 JIT、LLC 和 CBE,這些目標將傳遞 Makefile 中指定的程式選項

cd llvm/test/../../program
make bugpoint-jit

bugpoint 成功執行後,您將看到兩個位元碼檔案:一個可以使用 C 後端編譯的*安全*檔案,以及一個 LLC 或 JIT 錯誤產生程式碼並因此導致錯誤的*測試*檔案。

若要重現 bugpoint 找到的錯誤,只需執行以下操作

  1. 從安全位元碼檔案重新產生共享物件

    llc -march=c safe.bc -o safe.c
    gcc -shared safe.c -o safe.so
    
  2. 如果除錯 LLC,則將測試位元碼編譯為原生程式碼並與共享物件連結

    llc test.bc -o test.s
    gcc test.s safe.so -o test.llc
    ./test.llc [program options]
    
  3. 如果除錯 JIT,則載入共享物件並提供測試位元碼

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