FileCheck - 彈性模式比對檔案驗證器

概要

FileCheck match-filename [–check-prefix=XXX] [–strict-whitespace]

描述

FileCheck 讀取兩個檔案(一個來自標準輸入,另一個在命令列中指定),並使用其中一個來驗證另一個。此行為對於測試套件特別有用,測試套件想要驗證某些工具(例如 llc)的輸出是否包含預期的資訊(例如,來自 esp 或任何有趣的 movsd)。這類似於使用 grep,但它針對在特定順序中在一個檔案中比對多個不同的輸入進行了最佳化。

match-filename 檔案指定包含要比對的模式的檔案。要驗證的檔案從標準輸入讀取,除非使用 --input-file 選項。

選項

選項從環境變數 FILECHECK_OPTS 和命令列解析。

-help

印出命令列選項的摘要。

--check-prefix prefix

FileCheck 在 match-filename 的內容中搜尋要比對的模式。預設情況下,這些模式以 “CHECK:” 作為前綴。如果您想使用不同的前綴(例如,因為同一個輸入檔案正在檢查多個不同的工具或選項),--check-prefix 引數允許您指定(不帶尾隨的 “:”)一個或多個要比對的前綴。多個前綴對於可能因不同執行選項而更改的測試很有用,但大多數行保持不變。

FileCheck 不允許重複的前綴,即使其中一個是檢查前綴,另一個是註解前綴(請參閱下面的 --comment-prefixes)。

--check-prefixes prefix1,prefix2,...

--check-prefix 的別名,允許將多個前綴指定為逗號分隔的列表。

--comment-prefixes prefix1,prefix2,...

預設情況下,如果 match-filename 中的任何檢查前綴在同一行前面有 “COM:” 或 “RUN:”,FileCheck 將忽略它們的任何出現。 有關使用詳細資訊,請參閱 “COM:” 指令 章節。

如果這些預設註解前綴不適合您的測試環境,則可以透過 --comment-prefixes 覆寫它們。但是,不建議在 LLVM 基於 LIT 的測試套件中這樣做,如果它們都遵循一致的註解樣式,則應該更容易維護。在這種情況下,請考慮提出更改預設註解前綴的建議。

--allow-unused-prefixes

當使用 --check-prefix--check-prefixes 指定多個前綴,並且測試檔案中缺少其中一些前綴時,此選項控制行為。 如果為 true,則允許這樣做;如果為 false,FileCheck 將報告錯誤,列出缺少的字首。預設值為 false。

--input-file filename

要檢查的檔案(預設為 stdin)。

--match-full-lines

預設情況下,FileCheck 允許在行上的任何位置進行比對。此選項將要求所有肯定比對涵蓋整行。除非也指定了 --strict-whitespace,否則將忽略開頭和結尾的空白。(注意:來自 CHECK-NOT 的否定比對不受此選項的影響!)

傳遞此選項等同於在每個肯定檢查模式之前插入 {{^ *}}{{^}},以及之後插入 {{ *$}}{{$}}

--strict-whitespace

預設情況下,FileCheck 正規化輸入水平空白字元(空格和 Tab 字元),這會導致它忽略這些差異(空格將比對 Tab 字元)。--strict-whitespace 引數停用此行為。所有模式中的行尾序列都正規化為 UNIX 樣式的 \n

--ignore-case

預設情況下,FileCheck 使用區分大小寫的比對。此選項使 FileCheck 使用不區分大小寫的比對。

--implicit-check-not check-pattern

在肯定檢查之間新增指定模式的隱含否定檢查。此選項允許編寫更嚴格的測試,而無需使用 CHECK-NOT 填充它們。

例如,當測試來自沒有類似於 clang -verify 選項的工具的診斷訊息時,“--implicit-check-not warning:” 可能很有用。 使用此選項,FileCheck 將驗證輸入是否不包含任何 CHECK: 模式未涵蓋的警告。

--dump-input <value>

將輸入傾印到 stderr,新增表示目前已啟用診斷的註解。 當此選項多次出現時,下面列表中最早出現的 <value> 具有優先權。預設值為 fail

  • help - 解釋輸入傾印並退出

  • always - 總是傾印輸入

  • fail - 失敗時傾印輸入

  • never - 永不傾印輸入

--dump-input-context <N>

--dump-input 請求的傾印中,在 --dump-input-filter 指定的任何行之前和之後印出 <N> 行輸入行。當此選項多次出現時,指定的最大 <N> 具有優先權。預設值為 5。

--dump-input-filter <value>

--dump-input 請求的傾印中,僅印出 <value> 種類的輸入行,以及 --dump-input-context 指定的任何上下文。當此選項多次出現時,下面列表中最早出現的 <value> 具有優先權。當 --dump-input=fail 時,預設值為 error,當 --dump-input=always 時,預設值為 all

  • all - 所有輸入行

  • annotation-full - 帶有註解的輸入行

  • annotation - 帶有註解起始點的輸入行

  • error - 帶有錯誤註解起始點的輸入行

--enable-var-scope

啟用正則表達式變數的範圍。

名稱以 $ 開頭的變數被視為全域變數,並在整個檔案中保持設定。

所有其他變數在每次遇到 CHECK-LABEL 後都會變為未定義。

-D<VAR=VALUE>

使用可以在 CHECK: 行中使用的值 VALUE 設定 filecheck 模式變數 VAR

-D#<FMT>,<NUMVAR>=<NUMERIC EXPRESSION>

將格式為 FMT 的 filecheck 數字變數 NUMVAR 設定為評估 <NUMERIC EXPRESSION> 的結果,該結果可以在 CHECK: 行中使用。 有關支援的數字表達式的詳細資訊,請參閱 FileCheck 數字變數和表達式 章節。

-version

顯示此程式的版本號碼。

-v

印出良好的指令模式比對。但是,如果 -dump-input=fail-dump-input=always,則將這些比對作為輸入註解新增。

-vv

印出有助於診斷 FileCheck 內部問題的資訊,例如捨棄的重疊 CHECK-DAG: 比對、隱含的 EOF 模式比對以及沒有比對的 CHECK-NOT: 模式。暗示 -v。但是,如果 -dump-input=fail-dump-input=always,則僅將該資訊作為輸入註解新增。

--allow-deprecated-dag-overlap

在一組連續的 CHECK-DAG: 指令中啟用比對之間的重疊。此選項已棄用,僅在舊測試遷移到新的非重疊 CHECK-DAG: 實作時為了方便而提供。

--allow-empty

允許檢查空輸入。預設情況下,空輸入會被拒絕。

--color

在輸出中使用顏色(預設情況下自動偵測)。

結束狀態

如果 FileCheck 驗證檔案是否符合預期內容,則它會以 0 結束。否則,如果沒有,或者如果發生錯誤,它將以非零值結束。

教學

FileCheck 通常從 LLVM 迴歸測試中使用,在測試的 RUN 行中調用。從 RUN 行使用 FileCheck 的一個簡單範例看起來像這樣

; RUN: llvm-as < %s | llc -march=x86-64 | FileCheck %s

此語法表示將當前檔案(“%s”)管道傳輸到 llvm-as,將其管道傳輸到 llc,然後將 llc 的輸出管道傳輸到 FileCheck。 這表示 FileCheck 將根據指定的檔案名稱引數(“%s” 指定的原始 .ll 檔案)驗證其標準輸入(llc 輸出)。要了解其工作原理,讓我們看看 .ll 檔案的其餘部分(在 RUN 行之後)

define void @sub1(i32* %p, i32 %v) {
entry:
; CHECK: sub1:
; CHECK: subl
        %0 = tail call i32 @llvm.atomic.load.sub.i32.p0i32(i32* %p, i32 %v)
        ret void
}

define void @inc4(i64* %p) {
entry:
; CHECK: inc4:
; CHECK: incq
        %0 = tail call i64 @llvm.atomic.load.add.i64.p0i64(i64* %p, i64 1)
        ret void
}

您可以在此處看到註解中指定的一些 “CHECK:” 行。現在您可以看到檔案如何管道傳輸到 llvm-as,然後 llc,以及我們正在驗證的機器碼輸出。FileCheck 檢查機器碼輸出,以驗證它是否符合 “CHECK:” 行指定的內容。

CHECK:” 行的語法非常簡單:它們是必須按順序出現的固定字串。FileCheck 預設忽略水平空白字元差異(例如,允許空格比對 Tab 字元),但除此之外,“CHECK:” 行的內容必須完全比對測試檔案中的某些內容。

FileCheck(與 grep 相比)的一個優點是它允許將測試案例合併到邏輯組中。例如,由於上面的測試正在檢查 “sub1:” 和 “inc4:” 標籤,因此除非在這些標籤之間有 “subl”,否則它將不會比對。如果它存在於檔案中的其他位置,則不會計入:“grep subl” 如果 “subl” 存在於檔案中的任何位置,則會比對。

FileCheck -check-prefix 選項

FileCheck -check-prefix 選項允許從一個 .ll 檔案驅動多個測試配置。這在許多情況下都很有用,例如,使用 llc 測試不同的架構變體。這是一個簡單的範例

; RUN: llvm-as < %s | llc -mtriple=i686-apple-darwin9 -mattr=sse41 \
; RUN:              | FileCheck %s -check-prefix=X32
; RUN: llvm-as < %s | llc -mtriple=x86_64-apple-darwin9 -mattr=sse41 \
; RUN:              | FileCheck %s -check-prefix=X64

define <4 x i32> @pinsrd_1(i32 %s, <4 x i32> %tmp) nounwind {
        %tmp1 = insertelement <4 x i32>; %tmp, i32 %s, i32 1
        ret <4 x i32> %tmp1
; X32: pinsrd_1:
; X32:    pinsrd $1, 4(%esp), %xmm0

; X64: pinsrd_1:
; X64:    pinsrd $1, %edi, %xmm0
}

在這種情況下,我們正在測試我們是否透過 32 位和 64 位程式碼產生獲得了預期的程式碼產生。

“COM:” 指令

有時您想要停用 FileCheck 指令而不完全刪除它,或者您想要編寫註解來提及指令的名稱。 “COM:” 指令使其易於做到這一點。例如,您可能有

; X32: pinsrd_1:
; X32:    pinsrd $1, 4(%esp), %xmm0

; COM: FIXME: X64 isn't working correctly yet for this part of codegen, but
; COM: X64 will have something similar to X32:
; COM:
; COM:   X64: pinsrd_1:
; COM:   X64:    pinsrd $1, %edi, %xmm0

如果沒有 “COM:”,您將需要使用一些重寫和指令語法混淆的組合來防止 FileCheck 將上面註解掉的 “X32:” 和 “X64:” 識別為指令。 此外,已經提出了 FileCheck 診斷,可能會抱怨上面沒有尾隨 “:” 的 “X64” 出現,因為它們看起來像指令錯字。對於測試作者來說,躲避所有這些問題可能很乏味,而指令語法混淆可能會使測試程式碼的目的不明確。 “COM:” 避免了所有這些問題。

一些重要的使用注意事項

  • 另一個指令的模式中的 “COM:” *不會* 註解掉模式的其餘部分。 例如

    ; X32: pinsrd $1, 4(%esp), %xmm0 COM: This is part of the X32 pattern!
    

    如果您需要暫時註解掉指令模式的一部分,請將其移到另一行。原因是 FileCheck 以與任何其他指令相同的方式解析 “COM:”:只有行上的第一個指令被識別為指令。

  • 為了 LIT 的緣故,FileCheck 將 “RUN:” 視為與 “COM:” 相同。 如果這不適合您的測試環境,請參閱 --comment-prefixes

  • 如果 “COM”、“RUN” 或任何使用者定義的註解前綴與常見的檢查指令字尾(例如 “-NEXT:” 或 “-NOT:”,如下所述)結合使用,FileCheck 不會將其識別為註解指令。 FileCheck 將此類組合視為純文字。 如果它需要作為您的測試環境的註解指令,請使用 --comment-prefixes 將其定義為註解指令。

“CHECK-NEXT:” 指令

有時您想要比對行,並希望驗證比對是否發生在完全連續的行上,並且它們之間沒有其他行。在這種情況下,您可以使用 “CHECK:” 和 “CHECK-NEXT:” 指令來指定此項。如果您指定了自訂檢查前綴,只需使用 “<PREFIX>-NEXT:” 即可。例如,如下所示的內容會按您的預期工作

define void @t2(<2 x double>* %r, <2 x double>* %A, double %B) {
     %tmp3 = load <2 x double>* %A, align 16
     %tmp7 = insertelement <2 x double> undef, double %B, i32 0
     %tmp9 = shufflevector <2 x double> %tmp3,
                            <2 x double> %tmp7,
                            <2 x i32> < i32 0, i32 2 >
     store <2 x double> %tmp9, <2 x double>* %r, align 16
     ret void

; CHECK:          t2:
; CHECK:             movl    8(%esp), %eax
; CHECK-NEXT:        movapd  (%eax), %xmm0
; CHECK-NEXT:        movhpd  12(%esp), %xmm0
; CHECK-NEXT:        movl    4(%esp), %eax
; CHECK-NEXT:        movapd  %xmm0, (%eax)
; CHECK-NEXT:        ret
}

除非 “CHECK-NEXT:” 指令與前一個指令之間只有一個換行符,否則它們會拒絕輸入。 “CHECK-NEXT:” 不能是檔案中的第一個指令。

“CHECK-SAME:” 指令

有時您想要比對行,並希望驗證比對是否與先前的比對發生在同一行上。在這種情況下,您可以使用 “CHECK:” 和 “CHECK-SAME:” 指令來指定此項。如果您指定了自訂檢查前綴,只需使用 “<PREFIX>-SAME:” 即可。

CHECK-SAME:” 與 “CHECK-NOT:”(如下所述)結合使用時特別強大。

例如,以下內容會按您的預期工作

!0 = !DILocation(line: 5, scope: !1, inlinedAt: !2)

; CHECK:       !DILocation(line: 5,
; CHECK-NOT:               column:
; CHECK-SAME:              scope: ![[SCOPE:[0-9]+]]

如果 “CHECK-SAME:” 指令與前一個指令之間有任何換行符,它們會拒絕輸入。

CHECK-SAME:” 也可用於避免為不相關的欄位編寫比對器。例如,假設您正在編寫一個測試,該測試解析一個工具,該工具產生如下所示的輸出

Name: foo
Field1: ...
Field2: ...
Field3: ...
Value: 1

Name: bar
Field1: ...
Field2: ...
Field3: ...
Value: 2

Name: baz
Field1: ...
Field2: ...
Field3: ...
Value: 1

要編寫一個驗證 foo 的值為 1 的測試,您可以先編寫這個

CHECK: Name: foo
CHECK: Value: 1{{$}}

但是,這將是一個糟糕的測試:如果 foo 的值發生變化,測試仍然會通過,因為 “CHECK: Value: 1” 行將比對來自 baz 的值。 要修正此問題,您可以為每個 FieldN: 行新增 CHECK-NEXT 比對器,但這將很冗長,並且需要在新增 Field4 時進行更新。 使用 “CHECK-SAME:” 比對器編寫測試的更簡潔方法如下

CHECK:      Name: foo
CHECK:      Value:
CHECK-SAME:        {{ 1$}}

這驗證了*下次* “Value:” 出現在輸出中時,其值為 1

注意:“CHECK-SAME:” 不能是檔案中的第一個指令。

“CHECK-EMPTY:” 指令

如果您需要檢查下一行是否沒有任何內容,甚至沒有空白字元,則可以使用 “CHECK-EMPTY:” 指令。

declare void @foo()

declare void @bar()
; CHECK: foo
; CHECK-EMPTY:
; CHECK-NEXT: bar

就像 “CHECK-NEXT:” 一樣,如果它在找到下一個空白行之前有多個換行符,則指令將失敗,並且它不能是檔案中的第一個指令。

“CHECK-NOT:” 指令

CHECK-NOT:” 指令用於驗證字串不會在兩個比對之間(或在第一個比對之前,或在最後一個比對之後)出現。例如,要驗證轉換是否移除了載入,可以使用如下所示的測試

define i8 @coerce_offset0(i32 %V, i32* %P) {
  store i32 %V, i32* %P

  %P2 = bitcast i32* %P to i8*
  %P3 = getelementptr i8* %P2, i32 2

  %A = load i8* %P3
  ret i8 %A
; CHECK: @coerce_offset0
; CHECK-NOT: load
; CHECK: ret i8
}

“CHECK-COUNT:” 指令

如果您需要重複比對具有相同模式的多行,您可以根據需要多次重複使用普通的 CHECK:。 如果這看起來太枯燥,您可以改用計數檢查 “CHECK-COUNT-<num>:”,其中 <num> 是正十進制數字。 它將完全比對模式 <num> 次,不多也不少。如果您指定了自訂檢查前綴,只需使用 “<PREFIX>-COUNT-<num>:” 即可獲得相同的效果。 這是一個簡單的範例

Loop at depth 1
Loop at depth 1
Loop at depth 1
Loop at depth 1
  Loop at depth 2
    Loop at depth 3

; CHECK-COUNT-6: Loop at depth {{[0-9]+}}
; CHECK-NOT:     Loop at depth {{[0-9]+}}

“CHECK-DAG:” 指令

如果需要比對未按嚴格循序順序出現的字串,則可以使用 “CHECK-DAG:” 在兩個比對之間(或在第一個比對之前,或在最後一個比對之後)驗證它們。 例如,clang 以相反的順序發出 vtable 全域變數。 使用 CHECK-DAG:,我們可以保持檢查的自然順序

// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s

struct Foo { virtual void method(); };
Foo f;  // emit vtable
// CHECK-DAG: @_ZTV3Foo =

struct Bar { virtual void method(); };
Bar b;
// CHECK-DAG: @_ZTV3Bar =

CHECK-NOT: 指令可以與 CHECK-DAG: 指令混合使用,以排除周圍 CHECK-DAG: 指令之間的字串。 因此,周圍的 CHECK-DAG: 指令無法重新排序,即在 CHECK-NOT: 之前的與 CHECK-DAG: 比對的所有出現次數不得落在 CHECK-NOT: 之後的與 CHECK-DAG: 比對的出現次數之後。 例如,

; CHECK-DAG: BEFORE
; CHECK-NOT: NOT
; CHECK-DAG: AFTER

這種情況將拒絕 BEFORE 出現在 AFTER 之後的輸入字串。

使用捕獲的變數,CHECK-DAG: 能夠比對 DAG 的有效拓撲排序,其中邊緣從變數的定義到其使用。 例如,當您的測試案例需要比對指令排程器的不同輸出序列時,它很有用。 例如,

; CHECK-DAG: add [[REG1:r[0-9]+]], r1, r2
; CHECK-DAG: add [[REG2:r[0-9]+]], r3, r4
; CHECK:     mul r5, [[REG1]], [[REG2]]

在這種情況下,將允許這兩個 add 指令的任何順序。

如果您在同一個 CHECK-DAG: 區塊中定義*和*使用變數,請注意定義規則可以在其使用*之後*比對。

因此,例如,下面的程式碼將通過

; CHECK-DAG: vmov.32 [[REG2:d[0-9]+]][0]
; CHECK-DAG: vmov.32 [[REG2]][1]
vmov.32 d0[1]
vmov.32 d0[0]

而另一個程式碼則不會

; CHECK-DAG: vmov.32 [[REG2:d[0-9]+]][0]
; CHECK-DAG: vmov.32 [[REG2]][1]
vmov.32 d1[1]
vmov.32 d0[0]

雖然這可能非常有用,但也存在危險,因為在暫存器序列的情況下,您必須具有強順序(先讀後寫、先複製後使用等)。 如果您的測試正在尋找的定義不比對(由於編譯器中的錯誤),則它可能會在離使用更遠的地方比對,並掩蓋真正的錯誤。

在這些情況下,為了強制執行順序,請在 DAG 區塊之間使用非 DAG 指令。

CHECK-DAG: 指令會跳過與同一個 CHECK-DAG: 區塊中任何先前的 CHECK-DAG: 指令的比對重疊的比對。 這種非重疊行為不僅與其他指令一致,而且對於處理非唯一字串或模式的集合也是必要的。 例如,以下指令在平行程式(例如 OpenMP 執行階段)中尋找兩個任務的未排序日誌條目

// CHECK-DAG: [[THREAD_ID:[0-9]+]]: task_begin
// CHECK-DAG: [[THREAD_ID]]: task_end
//
// CHECK-DAG: [[THREAD_ID:[0-9]+]]: task_begin
// CHECK-DAG: [[THREAD_ID]]: task_end

即使模式相同,甚至日誌條目的文字相同,第二對指令也保證不會比對與第一對相同的日誌條目,因為執行緒 ID 設法被重複使用。

“CHECK-LABEL:” 指令

有時,在包含多個測試(分為邏輯區塊)的檔案中,一個或多個 CHECK: 指令可能會因比對稍後區塊中的行而意外成功。 雖然通常最終會產生錯誤,但標記為導致錯誤的檢查實際上可能與問題的實際來源沒有任何關係。

為了在這些情況下產生更好的錯誤訊息,可以使用 “CHECK-LABEL:” 指令。 它的處理方式與普通的 CHECK 指令相同,只是 FileCheck 額外假設指令比對的行也不能與 match-filename 中存在的任何其他檢查比對; 這旨在用於包含標籤或其他唯一識別碼的行。 從概念上講,CHECK-LABEL 的存在將輸入流劃分為單獨的區塊,每個區塊都獨立處理,防止一個區塊中的 CHECK: 指令比對另一個區塊中的行。 如果 --enable-var-scope 生效,則所有局部變數都會在區塊開始時清除。

例如,

define %struct.C* @C_ctor_base(%struct.C* %this, i32 %x) {
entry:
; CHECK-LABEL: C_ctor_base:
; CHECK: mov [[SAVETHIS:r[0-9]+]], r0
; CHECK: bl A_ctor_base
; CHECK: mov r0, [[SAVETHIS]]
  %0 = bitcast %struct.C* %this to %struct.A*
  %call = tail call %struct.A* @A_ctor_base(%struct.A* %0)
  %1 = bitcast %struct.C* %this to %struct.B*
  %call2 = tail call %struct.B* @B_ctor_base(%struct.B* %1, i32 %x)
  ret %struct.C* %this
}

define %struct.D* @D_ctor_base(%struct.D* %this, i32 %x) {
entry:
; CHECK-LABEL: D_ctor_base:

在這種情況下,使用 CHECK-LABEL: 指令可確保三個 CHECK: 指令僅接受與 @C_ctor_base 函數主體對應的行,即使模式比對稍後在檔案中找到的行也是如此。 此外,如果這三個 CHECK: 指令之一失敗,FileCheck 將透過繼續下一個區塊來恢復,從而在單個調用中偵測到多個測試失敗。

不需要 CHECK-LABEL: 指令包含與來源或輸出語言中的實際語法標籤對應的字串:它們只需唯一地比對正在驗證的檔案中的單行即可。

CHECK-LABEL: 指令不能包含變數定義或使用。

指令修飾符

指令修飾符可以透過在指令後跟 {<modifier>} 來附加到指令,其中 <modifier> 唯一支援的值是 LITERAL

LITERAL 指令修飾符可用於執行文字比對。 修飾符會導致指令不識別任何語法來執行正則表達式比對、變數捕獲或任何替換。 當要比對的文字需要過多的跳脫字元時,這很有用。 例如,以下內容將執行文字比對,而不是將其視為正則表達式

Input: [[[10, 20]], [[30, 40]]]
Output %r10: [[10, 20]]
Output %r10: [[30, 40]]

; CHECK{LITERAL}: [[[10, 20]], [[30, 40]]]
; CHECK-DAG{LITERAL}: [[30, 40]]
; CHECK-DAG{LITERAL}: [[10, 20]]

FileCheck 正則表達式比對語法

所有 FileCheck 指令都需要一個要比對的模式。對於 FileCheck 的大多數用途來說,固定字串比對已完全足夠。但對於某些情況,則需要更彈性的比對形式。為了支援這一點,FileCheck 允許您在比對字串中指定正規表示式,並用雙重花括號括起來:{{您的正規表示式}}。FileCheck 實作了 POSIX 正規表示式比對器;它支援擴展 POSIX 正規表示式 (ERE)。因為我們希望在大多數情況下使用固定字串比對,所以 FileCheck 的設計使其能夠混合搭配固定字串比對和正規表示式。這讓您可以撰寫像這樣的程式碼:

; CHECK: movhpd      {{[0-9]+}}(%esp), {{%xmm[0-7]}}

在這種情況下,任何來自 ESP 暫存器的偏移量以及任何 xmm 暫存器都會被允許。

由於正規表示式被雙重花括號括起來,因此它們在視覺上很明顯,而且您不需要像在 C 語言中那樣在雙重花括號內使用跳脫字元。在極少數情況下,如果您想從輸入中明確地比對雙重花括號,您可以使用像 {{[}][}]}} 這樣不太美觀的模式。或者,如果您使用的是重複計數語法,例如 [[:xdigit:]]{8} 來比對正好 8 個十六進位數字,您將需要像這樣新增括號 {{([[:xdigit:]]{8})}},以避免與 FileCheck 的結束雙重花括號混淆。

FileCheck 字串替換區塊

通常,比對一個模式,然後驗證它稍後在檔案中再次出現是很有用的。對於程式碼產生測試來說,這對於允許任何暫存器可能很有用,但要驗證該暫存器在稍後是否被一致地使用。為了做到這一點,FileCheck 支援字串替換區塊,允許定義字串變數並將其替換到模式中。以下是一個簡單的範例:

; CHECK: test5:
; CHECK:    notw     [[REGISTER:%[a-z]+]]
; CHECK:    andw     {{.*}}[[REGISTER]]

第一行檢查行比對正規表示式 %[a-z]+,並將其捕獲到字串變數 REGISTER 中。第二行驗證 REGISTER 中的任何內容稍後在檔案中 “andw” 之後出現。FileCheck 字串替換區塊始終包含在 [[ ]] 配對中,並且字串變數名稱可以使用正規表示式 \$[a-zA-Z_][a-zA-Z0-9_]* 來形成。如果名稱後跟一個冒號,則表示它是變數的定義;否則,它就是一個替換。

FileCheck 變數可以被多次定義,而替換總是取得最新的值。變數也可以在它們被定義的同一行稍後被替換。例如:

; CHECK: op [[REG:r[0-9]+]], [[REG]]

如果您希望 op 的運算元是相同的暫存器,並且不在意到底是哪個暫存器,這會很有用。

如果 --enable-var-scope 生效,則名稱以 $ 開頭的變數會被視為全域變數。所有其他變數都是區域變數。所有區域變數在每個 CHECK-LABEL 區塊的開頭都會變成未定義。全域變數不受 CHECK-LABEL 的影響。這使得更容易確保個別測試不受先前測試中設定的變數影響。

FileCheck 數字替換區塊

FileCheck 也支援數字替換區塊,允許定義數字變數,並透過數字替換檢查滿足基於這些變數的數字表達式約束的數字值。這允許 CHECK: 指令驗證兩個數字之間的數字關係,例如需要使用連續的暫存器。

捕獲數字值的語法是 [[#%<fmtspec>,<NUMVAR>:]],其中:

  • %<fmtspec>, 是一個可選的格式指定符,用於指示要比對的數字格式以及預期的最小位數。

  • <NUMVAR>: 是從捕獲的值中可選地定義變數 <NUMVAR>

<fmtspec> 的語法是:#.<precision><轉換指定符>,其中:

  • # 是一個可選的標記,適用於十六進位值(請參閱下面的 <轉換指定符>),它要求比對的值以 0x 作為前綴。

  • .<precision> 是一個可選的 printf 風格的精度指定符,其中 <precision> 指示比對的值必須具有的最小位數,如果需要,預期要有前導零。

  • <轉換指定符> 是一個可選的 scanf 風格的轉換指定符,用於指示要比對的數字格式(例如,十六進位數字)。目前接受的格式指定符有 %u%d%x%X。如果缺席,格式指定符預設為 %u

例如:

; CHECK: mov r[[#REG:]], 0x[[#%.8X,ADDR:]]

將會比對 mov r5, 0x0000FEFE,並將 REG 設定為值 5,將 ADDR 設定為值 0xFEFE。請注意,由於精度的關係,它將無法比對 mov r5, 0xFEFE

由於數字變數定義是可選的,因此可以僅檢查給定格式中是否存在數字值。當值本身沒有用處時,這可能很有用,例如:

; CHECK-NOT: mov r0, r[[#]]

檢查值是否是合成的而不是移動的。

數字替換的語法是 [[#%<fmtspec>, <constraint> <expr>]],其中:

  • <fmtspec> 是與定義變數相同的格式指定符,但在這種情況下,它指示應該如何比對數字表達式值。如果缺席,格式指定符的兩個組成部分都會從表達式約束使用的數字變數的比對格式中推斷出來(如果有的話),如果沒有使用數字變數,則預設為 %u,表示該值應該是無符號的,並且沒有前導零。如果多個數字變數的格式指定符之間存在衝突,則轉換指定符會變成強制性的,但精度指定符仍然是可選的。

  • <constraint> 是描述要比對的值必須如何與數字表達式的值相關聯的約束。目前唯一接受的約束是 ==,用於完全比對,如果未提供 <constraint>,則它是預設值。當 <expr> 為空時,不得指定比對約束。

  • <expr> 是一個表達式。表達式反過來被遞迴地定義為:

    • 一個數字運算元,或

    • 一個表達式,後跟一個運算子和一個數字運算元。

    數字運算元是先前定義的數字變數、整數常值或函數。在任何這些元素之前、之後和之間都接受空格。數字運算元具有 64 位元精度。溢位和下溢會被拒絕。不支援運算子優先順序,但可以使用括號來更改求值順序。

支援的運算子有:

  • + - 傳回其兩個運算元的總和。

  • - - 傳回其兩個運算元的差。

函數呼叫的語法是 <name>(<arguments>),其中:

  • name 是一個預定義的字串常值。接受的值為:

    • add - 傳回其兩個運算元的總和。

    • div - 傳回其兩個運算元的商。

    • max - 傳回其兩個運算元中最大的一個。

    • min - 傳回其兩個運算元中最小的一個。

    • mul - 傳回其兩個運算元的乘積。

    • sub - 傳回其兩個運算元的差。

  • <arguments> 是一個以逗號分隔的表達式列表。

例如:

; CHECK: load r[[#REG:]], [r0]
; CHECK: load r[[#REG+1]], [r1]
; CHECK: Loading from 0x[[#%x,ADDR:]]
; CHECK-SAME: to 0x[[#ADDR + 7]]

上面的範例將會比對文字:

load r5, [r0]
load r6, [r1]
Loading from 0xa0463440 to 0xa0463447

但不會比對文字:

load r5, [r0]
load r7, [r1]
Loading from 0xa0463440 to 0xa0463443

由於 7 不等於 5 + 1,並且 a0463443 不等於 a0463440 + 7

數字變數也可以被定義為數字表達式的結果,在這種情況下,數字表達式約束會被檢查,如果驗證通過,則變數會被賦值。因此,用於檢查數字表達式並將其值捕獲到數字變數中的統一語法是 [[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]],每個元素如先前所述。可以使用此語法透過使用變數而不是值來使測試案例更具自我描述性:

; CHECK: mov r[[#REG_OFFSET:]], 0x[[#%X,FIELD_OFFSET:12]]
; CHECK-NEXT: load r[[#]], [r[[#REG_BASE:]], r[[#REG_OFFSET]]]

這將會比對:

mov r4, 0xC
load r6, [r5, r4]

--enable-var-scope 選項對數字變數的作用與對字串變數的作用相同。

重要提示:在其目前的實作中,表達式不能使用在同一個 CHECK 指令中較早定義的數字變數。

FileCheck 虛擬數字變數

有時需要驗證包含比對檔案行號的輸出,例如在測試編譯器診斷時。這會引入比對檔案結構的某種脆弱性,因為 “CHECK:” 行包含相同檔案中的絕對行號,這些行號必須在由於文字新增或刪除而導致行號變更時進行更新。

為了支援這種情況,FileCheck 表達式理解 @LINE 虛擬數字變數,它會評估為找到它的 CHECK 模式的行號。

這樣,比對模式可以放在相關測試行的附近,並包含相對行號參考,例如:

// CHECK: test.cpp:[[# @LINE + 4]]:6: error: expected ';' after top level declarator
// CHECK-NEXT: {{^int a}}
// CHECK-NEXT: {{^     \^}}
// CHECK-NEXT: {{^     ;}}
int a

為了支援 @LINE 作為特殊字串變數的舊版用法,FileCheck 也接受以下使用字串替換區塊語法的 @LINE[[@LINE]][[@LINE+<offset>]][[@LINE-<offset>]],括號內沒有任何空格,其中 offset 是一個整數。

比對換行字元

為了在正規表示式中比對換行字元,可以使用字元類別 [[:space:]]。例如,以下模式:

// CHECK: DW_AT_location [DW_FORM_sec_offset] ([[DLOC:0x[0-9a-f]+]]){{[[:space:]].*}}"intd"

比對以下形式的輸出(來自 llvm-dwarfdump):

DW_AT_location [DW_FORM_sec_offset]   (0x00000233)
DW_AT_name [DW_FORM_strp]  ( .debug_str[0x000000c9] = "intd")

讓我們將 FileCheck 變數 DLOC 設定為所需的值 0x00000233,從緊接在 “intd” 行之前的行中提取。