FILECHECK(1)

๐Ÿ“ฆ
version 16.0.6 ยท 3 versions
, in ubuntu noble

๐ŸŽ€ FILECHECK(1) ๐ŸŽ€

okay bestie, this is the LLVM manual page for FILECHECK(1) ๐Ÿ’– it's all about the FileCheck tool, so let's get into it โœจ

โœจ NAME โœจ

FileCheck โ€” a flexible pattern-matching file verifier ๐Ÿฆ‹

๐Ÿ’… SYNOPSIS ๐Ÿ’…

FileCheck match-filename [โ€“check-prefix=XXX] [โ€“strict-whitespace]

๐Ÿ“– DESCRIPTION ๐Ÿ“–

okay so FileCheck reads two files, one from standard input and one you pass on the command line, and it uses one of them to verify the other ๐Ÿ”ฅ this is literally the move for the testsuite, which needs to confirm that the output of some tool (e.g. llc) has the info it's supposed to, like a movsd from esp or whatever is giving main character energy ๐Ÿ’… it's kinda like grep, but it's built for matching a bunch of different inputs inside one file, in a specific order ๐ŸŒธ

The match-filename file is the one with the patterns to match ๐ŸŽ€ the file to verify gets read from standard input unless you use the --input-file option ๐Ÿซถ

๐ŸŒˆ OPTIONS ๐ŸŒˆ

Options get parsed from the environment variable FILECHECK_OPTS and from the command line too ๐Ÿ’–

  • -help โ€” prints a summary of all the command line options โœจ
  • --check-prefix prefix โ€” FileCheck goes digging through match-filename for patterns to match ๐Ÿ” by default those patterns are prefixed with CHECK: ๐Ÿ’œ if you want a different prefix (maybe the same input file is checking multiple tools or options), --check-prefix lets you pick one or more prefixes, written without the trailing : ๐Ÿฆ‹ multiple prefixes are great for tests that change between run options but mostly stay the same ๐Ÿ“ FileCheck doesn't allow duplicate prefixes, even if one is a check prefix and one is a comment prefix (see --comment-prefixes below) ๐Ÿ™ˆ
  • --check-prefixes prefix1,prefix2,... โ€” an alias of --check-prefix that lets you list several prefixes separated by commas ๐Ÿงš
  • --comment-prefixes prefix1,prefix2,... โ€” by default, FileCheck ignores any check prefix in match-filename if it's preceded on the same line by COM: or RUN: ๐Ÿ™ˆ check out the section The โ€œCOM:โ€ directive for the deets ๐Ÿ’… you can override those default comment prefixes with --comment-prefixes if they don't fit your testing setup ๐ŸŒธ tbh, doing that is not recommended in LLVM's LIT-based test suites ๐Ÿ™ƒ those should be way easier to maintain when everyone follows one comment style ๐Ÿ’œ if the defaults feel wrong, the better move is proposing a change to the default comment prefixes instead โœจ
  • --allow-unused-prefixes โ€” this one controls what happens when you use more than one prefix (via --check-prefix or --check-prefixes) and some of those prefixes are missing from the test file ๐Ÿฅบ if it's true, that's allowed ๐Ÿ’– if it's false, FileCheck throws an error and lists the missing prefixes ๐Ÿ’€ the default is false ๐Ÿซถ
  • --input-file filename โ€” the file to check ๐Ÿ“ฅ defaults to stdin
  • --match-full-lines โ€” by default FileCheck is chill and lets matches happen anywhere on a line ๐Ÿฆ‹ this option makes every positive match cover an entire line ๐Ÿ”ฅ leading and trailing whitespace gets ignored, unless --strict-whitespace is also specified โœจ (heads up: negative matches from CHECK-NOT are not affected by this option! ๐Ÿšจ) passing this option is the same as sticking {{^ *}} or {{^}} before, and {{ *$}} or {{$}} after, every positive check pattern ๐Ÿง 
  • --strict-whitespace โ€” by default FileCheck canonicalizes horizontal whitespace (spaces and tabs), so it ignores those differences, meaning a space will match a tab ๐Ÿซ  this flag turns that behaviour off ๐Ÿšซ end-of-line sequences get canonicalized to UNIX-style \n in all modes ๐Ÿง
  • --ignore-case โ€” the default is case-sensitive matching ๐Ÿ™… this makes FileCheck do case-insensitive matching instead ๐ŸŒˆ
  • --implicit-check-not check-pattern โ€” adds implicit negative checks for the patterns you give, sitting between your positive checks ๐Ÿ•ต๏ธ you can write stricter tests this way without stuffing them full of CHECK-NOTs ๐Ÿ’… for example, --implicit-check-not warning: is super useful for testing diagnostic messages from tools that don't have an option like clang -verify ๐Ÿšจ with this option, FileCheck verifies that the input doesn't contain any warnings that aren't covered by a CHECK: pattern ๐Ÿ‘‘
  • --dump-input <value> โ€” dumps the input to stderr, with annotations for whatever diagnostics are currently enabled ๐Ÿงพ if you use this option more than once, the <value> that shows up earliest in the list below wins ๐Ÿ‘‘ the default is fail ๐Ÿ’€
    • help โ€” explains the input dump and quits ๐Ÿ“š
    • always โ€” always dumps the input ๐Ÿ“ฃ
    • fail โ€” dumps the input on failure โŒ
    • never โ€” never dumps the input ๐Ÿ™…
  • --dump-input-context <N> โ€” in the dump that --dump-input asks for, prints <N> input lines before and <N> input lines after any lines picked by --dump-input-filter ๐Ÿ” with multiple occurrences, the largest <N> wins ๐Ÿ’ช the default is 5 ๐Ÿ–๏ธ
  • --dump-input-filter <value> โ€” in the --dump-input dump, prints only input lines of kind <value>, plus whatever context --dump-input-context asks for ๐ŸŽฏ with multiple occurrences, the <value> that appears earliest in the list below wins ๐Ÿ‘‘ the default is error when --dump-input=fail, and all when --dump-input=always ๐Ÿ’…
    • all โ€” all input lines ๐ŸŒ
    • annotation-full โ€” input lines with annotations ๐Ÿท๏ธ
    • annotation โ€” input lines with starting points of annotations ๐Ÿ“
    • error โ€” input lines with starting points of error annotations ๐Ÿšจ
  • --enable-var-scope โ€” turns on scope for regex variables ๐ŸŒธ variables with names that start with $ are considered global and stay set throughout the file ๐ŸŒ all other variables get undefined after each CHECK-LABEL they run into ๐Ÿงน
  • -D<VAR=VALUE> โ€” sets a FileCheck pattern variable VAR to the value VALUE, which you can then use in CHECK: lines โœจ
  • -D#<FMT>,<NUMVAR>=<NUMERIC EXPRESSION> โ€” sets a FileCheck numeric variable NUMVAR of matching format FMT to the result of evaluating <NUMERIC EXPRESSION>, which you can use in CHECK: lines ๐Ÿ”ข check out the section FileCheck Numeric Variables and Expressions for the details on supported numeric expressions ๐Ÿ“š
  • -version โ€” shows the version number of this program ๐Ÿท๏ธ
  • -v โ€” prints good directive pattern matches ๐Ÿฅฐ but if -dump-input=fail or -dump-input=always is set, those matches get added as input annotations instead ๐Ÿฆ‹
  • -vv โ€” prints info that helps diagnose internal FileCheck issues ๐Ÿ”ง like discarded overlapping CHECK-DAG: matches, implicit EOF pattern matches, and CHECK-NOT: patterns that don't have matches ๐Ÿ•ต๏ธ it implies -v โœจ but if -dump-input=fail or -dump-input=always is set, that info gets added as input annotations instead ๐Ÿซถ
  • --allow-deprecated-dag-overlap โ€” enables overlapping among matches in a group of consecutive CHECK-DAG: directives ๐ŸŒ€
โš ๏ธ this option is deprecated! it's only here for convenience while old tests get migrated to the new non-overlapping CHECK-DAG: implementation ๐Ÿฅฒ
  • --allow-empty โ€” allows checking empty input ๐Ÿซ™ by default, empty input gets rejected ๐Ÿ™…โ€โ™€๏ธ
  • --color โ€” uses colours in the output ๐ŸŒˆ this is autodetected by default โœจ
; X64: pinsrd_1:
; X64:    pinsrd $1, %edi, %xmm0
}

Okay so in this one we're checking that we get the right codegen for both 32-bit and 64-bit, and it's giving full coverage โœจ๐Ÿ’…


๐ŸŽ€ The COM: directive ๐ŸŽ€

Sometimes you want to turn off a FileCheck directive without deleting it, or you want to leave a comment that mentions a directive by name. That's where COM: comes in, and it makes this so easy ๐Ÿซถ For example, you might have:

; 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

Without COM:, you'd have to reword stuff and mangle the directive syntax just so FileCheck doesn't treat those commented X32: and X64: lines as real directives ๐Ÿ˜ญ Plus, FileCheck diagnostics have been proposed that might yell at those X64 lines for not having the trailing :, because they'd look like directive typos ๐Ÿ’€ Dodging all of that is so tedious for the test author, and mangling the syntax makes the test code way less clear. COM: avoids all of it, no notes ๐Ÿฆ‹

A few important usage notes, bestie:

  • COM: sitting inside another directive's pattern does not comment out the rest of the pattern ๐Ÿ™ˆ For example:
; X32: pinsrd $1, 4(%esp), %xmm0 COM: This is part of the X32 pattern!

If you need to temporarily comment out part of a directive's pattern, move it onto another line instead. The reason is that FileCheck parses COM: the same way as any other directive, so only the first directive on the line gets recognized ๐Ÿงš

  • For the sake of LIT, FileCheck treats RUN: just like COM: ๐ŸŒˆ If that's not suitable for your test environment, check out --comment-prefixes.
  • FileCheck does not recognize COM, RUN, or any user-defined comment prefix as a comment directive if it's combined with one of the usual check directive suffixes, like -NEXT: or -NOT: (we'll get to those below) ๐Ÿ‘‘ Instead, FileCheck treats that combo as plain text. If it needs to work as a comment directive for your test environment, define it that way with --comment-prefixes.

๐Ÿฆ‹ The CHECK-NEXT: directive ๐Ÿฆ‹

Sometimes you want to match lines and make sure the matches happen on exactly consecutive lines, with no other lines squeezed in between ๐Ÿฅบ In that case, use a CHECK: and a CHECK-NEXT: directive together. If you've got a custom check prefix, just use <PREFIX>-NEXT: instead. Something like this works exactly how you'd expect:

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
}

Heads up ๐Ÿšจ CHECK-NEXT: directives will reject the input unless there's exactly one newline between them and the previous directive. And a CHECK-NEXT: can never be the first directive in a file.


๐ŸŒธ The CHECK-SAME: directive ๐ŸŒธ

Sometimes you want matches to land on the same line as the previous match. For that, pair a CHECK: with a CHECK-SAME: directive ๐Ÿ’– With a custom check prefix, it's <PREFIX>-SAME:.

CHECK-SAME: is seriously powerful when you team it up with CHECK-NOT: (more on that below) โœจ For example, this one works like a dream:

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

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

CHECK-SAME: directives will reject the input if there are any newlines between them and the previous directive ๐Ÿ™…โ€โ™€๏ธ

It's also super handy for skipping fields you don't care about ๐Ÿ“ Say you're testing a tool that spits out output like this:

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

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

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

To check that foo has the value 1, you might first try this:

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

But that's a bad test ๐Ÿ˜ฌ If foo's value changes, the test would still pass, because the CHECK: Value: 1 line would just latch onto the value from baz. You could add CHECK-NEXT matchers for every FieldN: line to fix it, but that's verbose and you'd have to update it every time Field4 shows up. The succinct move is the CHECK-SAME: matcher:

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

This checks that the next time Value: shows up in the output, it has the value 1 ๐Ÿ’…

Note: a CHECK-SAME: cannot be the first directive in a file.


๐Ÿงš The CHECK-EMPTY: directive ๐Ÿงš

If you need to check that the next line is totally empty, not even whitespace, use the CHECK-EMPTY: directive ๐Ÿซง

declare void @foo()

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

Just like CHECK-NEXT:, this one fails if there's more than one newline before it finds the next blank line, and it can't be the first directive in a file.


๐Ÿ™…โ€โ™€๏ธ The CHECK-NOT: directive ๐Ÿ™…โ€โ™€๏ธ

The CHECK-NOT: directive makes sure a string does not show up between two matches (or before the first match, or after the last one) ๐Ÿ”ฅ For example, if you want to prove a transformation removed a load, a test like this does the job:

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
}

๐Ÿ” The CHECK-COUNT: directive ๐Ÿ”

If you need to match the same pattern over and over, you can just repeat a plain CHECK: as many times as you need ๐Ÿ’… But if that feels boring, you can use a counted check, CHECK-COUNT-<num>:, where <num> is a positive decimal number ๐ŸŒˆ It matches the pattern exactly <num> times, no more and no less. With a custom check prefix, use <PREFIX>-COUNT-<num>: for the same effect.

Here's a simple example:

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]+}}

๐ŸŒˆ The CHECK-DAG: directive ๐ŸŒˆ

Sometimes the strings you need to match don't show up in a strictly sequential order. That's when CHECK-DAG: saves the day ๐Ÿฆ„ It verifies them between two matches (or before the first match, or after the last one). For example, clang emits vtable globals in reverse order, but using CHECK-DAG: we can keep our checks in the natural order:

// 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: directives can be mixed in with CHECK-DAG: directives to exclude strings between the surrounding CHECK-DAG: directives ๐Ÿ‘€ The catch is that the surrounding CHECK-DAG: directives can't be reordered. In other words, all the occurrences matching the CHECK-DAG: before CHECK-NOT: must not fall behind occurrences matching the CHECK-DAG: after CHECK-NOT:. For example:

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

This case will reject input strings where BEFORE occurs after AFTER ๐Ÿšซ

With captured variables, CHECK-DAG: can match valid topological orderings of a DAG, with edges going from the definition of a variable to its use ๐Ÿง  This is super useful when your tests need to match different output sequences from the instruction scheduler. For example:

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

In this case, any order of those two add instructions will be allowed โœจ

If you're defining and using variables in the same CHECK-DAG: block, be aware that the definition rule can match after its use ๐Ÿ˜ณ So, for instance, the code below will pass:

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

But this other code will not:

; CHECK-DAG: vmov.32 [[REG2:d[0-9]+]][0]
; CHECK-DAG: vmov.32 [[REG2]][1]
vmov.32 d1[1]
vmov.32 d0[0]
โš ๏ธ This can be very useful, but it's also dangerous! For register sequences you need a strong order (read before write, copy before use, and so on). If the definition your test is hunting for doesn't match (because of a bug in the compiler), it might match further away from the use and mask real bugs. ๐Ÿ’€ So, to enforce the order in those cases, use a non-DAG directive between the DAG-blocks.

A CHECK-DAG: directive skips matches that overlap the matches of any preceding CHECK-DAG: directives in the same CHECK-DAG: block ๐Ÿซถ This non-overlapping behavior is consistent with other directives, and it's also necessary for handling sets of non-unique strings or patterns. For example, these directives look for unordered log entries for two tasks in a parallel program, like the OpenMP runtime:

// 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

The second pair of directives is guaranteed not to match the same log entries as the first pair ๐Ÿซถ, even when the patterns are identical and the log text is identical too, because the thread ID manages to get reused. That's the drama ๐Ÿ˜ญ


โœจ The โ€œCHECK-LABEL:โ€ directive โœจ

Sometimes in a file with multiple tests split into logical blocks, one or more CHECK: directives can accidentally pass by matching lines in a later block ๐Ÿ’€. An error usually shows up eventually, but the check that gets blamed may have zero connection to the actual source of the problem. Not it ๐Ÿ™„.

To get better error messages, use the CHECK-LABEL: directive ๐ŸŽ€. It works exactly like a normal CHECK directive, except FileCheck makes an extra assumption: a line matched by it can't also be matched by any other check in match-filename. It's meant for lines with labels or other unique identifiers. Conceptually, CHECK-LABEL splits the input stream into separate blocks that are each processed independently, so a CHECK: in one block can't match a line in another. And if --enable-var-scope is in effect, all local variables get cleared at the beginning of the block ๐Ÿงน.

For example,

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:

Using CHECK-LABEL: here means the three CHECK: directives only accept lines from the body of the @C_ctor_base function, even if their patterns match lines found later in the file ๐Ÿ‘‘. Furthermore, if one of those three CHECK: directives fails, FileCheck recovers by continuing to the next block, so multiple test failures can be detected in a single invocation ๐ŸŒˆ.

There's no requirement that CHECK-LABEL: directives contain strings that correspond to actual syntactic labels in a source or output language. They just have to uniquely match a single line in the file being verified ๐Ÿฆ‹.

Heads up: CHECK-LABEL: directives cannot contain variable definitions or uses. No exceptions, bestie ๐Ÿ™…โ€โ™€๏ธ.

โœจ Directive modifiers โœจ

A directive modifier can be appended to a directive by following the directive with {<modifier>}, where the only supported value for <modifier> is LITERAL ๐Ÿ’….

The LITERAL directive modifier performs a literal match. It means the directive won't recognise any regex matching syntax, variable capture, or substitutions. This is useful when the text you want to match would otherwise need excessive escaping ๐Ÿ™ˆ. For example, the following performs literal matches instead of treating these as regular expressions:

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 Regex Matching Syntax โœจ

Every FileCheck directive takes a pattern to match. For most uses, fixed string matching is perfectly sufficient ๐Ÿ“. For some things you want something more flexible, so FileCheck lets you write regular expressions inside matching strings, surrounded by double braces: {{yourregex}} ๐Ÿงš. FileCheck implements a POSIX regular expression matcher and supports Extended POSIX regular expressions (ERE). Since fixed string matching covers most of what we do, FileCheck is built to mix and match fixed strings with regexes, so you can write stuff like this:

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

In this case, any offset from the ESP register is allowed, and any xmm register is allowed too ๐Ÿ’œ.

Regular expressions are wrapped in double braces, so they're visually distinct, and you don't need to escape characters inside the braces like you would in C โœจ. In the rare case you want to match double braces explicitly from the input, you can use something ugly like {{[}][}]}} as your pattern ๐Ÿ’€. Or, if you're using the repetition count syntax, like [[:xdigit:]]{8} to match exactly 8 hex digits, you need to add parentheses like this: {{([[:xdigit:]]{8})}}, so it doesn't get confused with FileCheck's closing double-brace ๐Ÿซถ.


โœจ FileCheck String Substitution Blocks โœจ

It's often useful to match a pattern and then verify that it shows up again later in the file ๐Ÿ‘€. For codegen tests, this lets you allow any register, but also verify that the same register is used consistently later. FileCheck supports string substitution blocks that let you define string variables and substitute them into patterns. Here's a simple example:

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

The first check line matches the regex %[a-z]+ and captures it into the string variable REGISTER. The second line verifies that whatever is in REGISTER shows up later in the file after an โ€œandwโ€ ๐Ÿ”ฅ.

FileCheck string substitution blocks are always wrapped in [[ ]] pairs, and string variable names can be formed with the regex [a-zA-Z_][a-zA-Z0-9_]*. If a colon follows the name, it's a definition of the variable. Otherwise, it's a substitution ๐ŸŽ€.

FileCheck variables can be defined multiple times, and substitutions always grab the latest value. You can also substitute a variable later on the same line it was defined on. For example:

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

This is handy if you want the operands of op to be the same register and don't care exactly which register it is ๐Ÿง .

If --enable-var-scope is in effect, variables whose names start with $ are considered global. All other variables are local. All local variables get undefined at the beginning of each CHECK-LABEL block. Global variables aren't affected by CHECK-LABEL. This makes it way easier to make sure individual tests aren't affected by variables set in preceding tests ๐ŸŒธ.


โœจ FileCheck Numeric Substitution Blocks โœจ

FileCheck also supports numeric substitution blocks, which let you define numeric variables and check for numeric values that satisfy a numeric expression constraint based on those variables, through a numeric substitution ๐Ÿ”ข. This lets CHECK: directives verify a numeric relation between two numbers, like making sure consecutive registers are used ๐Ÿฆ‹.

The syntax to capture a numeric value is [[#%<fmtspec>,<NUMVAR>:]], where:

  • %<fmtspec>, is an optional format specifier that sets the number format to match and the minimum number of digits to expect.
  • <NUMVAR>: is an optional definition of variable <NUMVAR> from the captured value.

The syntax of <fmtspec> is #.<precision><conversion specifier>, where:

  • # is an optional flag for hex values (see <conversion specifier> below) that requires the matched value to be prefixed by 0x.
  • .<precision> is an optional printf-style precision specifier, where <precision> is the minimum number of digits the matched value must have, expecting leading zeros if needed.
  • <conversion specifier> is an optional scanf-style conversion specifier that sets the number format to match (e.g. hex number). The currently accepted format specifiers are %u, %d, %x and %X. If it's absent, the format specifier defaults to %u.

For example:

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

would match mov r5, 0x0000FEFE and set REG to the value 5 and ADDR to the value 0xFEFE. Note that because of the precision, it would fail to match mov r5, 0xFEFE ๐Ÿ™…โ€โ™€๏ธ.

Since the numeric variable definition is optional, you can also just check that a numeric value is present in a given format. This is useful when the value itself isn't important, for instance:

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

This checks that a value is synthesized rather than moved around ๐Ÿ’….

The syntax of a numeric substitution is [[#%<fmtspec>, <constraint> <expr>]], where:

  • <fmtspec> is the same format specifier used for defining a variable, but here it describes how a numeric expression value should be matched. If it's absent, both parts of the format specifier are inferred from the matching format of the numeric variable(s) used by the expression constraint, if any. It defaults to %u if no numeric variable is used, meaning the value should be unsigned with no leading zeros. If several numeric variables conflict on format specifiers, the conversion specifier becomes mandatory, but the precision specifier stays optional ๐Ÿค“.
  • <constraint> describes how the value to match must relate to the value of the numeric expression. The only currently accepted constraint is == for an exact match, and it's the default if <constraint> isn't provided. No matching constraint must be specified when the <expr> is empty.
  • <expr> is an expression. An expression is recursively defined as:
    • a numeric operand, or
    • an expression followed by an operator and a numeric operand.

A numeric operand is a previously defined numeric variable, an integer literal, or a function. Spaces are accepted before, after, and between any of these elements. Numeric operands have 64-bit precision. Overflow and underflow are rejected. There's no support for operator precedence, but parentheses can be used to change the evaluation order ๐Ÿงฎ.

The supported operators are:

  • + - Returns the sum of its two operands.
  • - - Returns the difference of its two operands.

The syntax of a function call is <name>(<arguments>), where:

  • name is a predefined string literal. The accepted values are:
    • add - Returns the sum of its two operands.
    • div - Returns the quotient of its two operands.
    • max - Returns the largest of its two operands.
    • min - Returns the smallest of its two operands.
    • mul - Returns the product of its two operands.
    • sub - Returns the difference of its two operands.
  • <arguments> is a comma-separated list of expressions.

For example:

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

okay so this example would match this text ๐Ÿ’–

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

but it would NOT match this one ๐Ÿ™ˆ

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

Why? Because 7 is not equal to 5 + 1, and a0463443 is not equal to a0463440 + 7. Literally the math ain't mathing ๐Ÿ’€

Also, a numeric variable can be set to the result of a numeric expression. In that case, the expression constraint gets checked, and if it passes, the variable gets assigned that value. The unified syntax for both checking an expression and capturing its value into a numeric variable is [[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]], with each element working the way it was described earlier. This is so good for making your testcase self-describing, because you can use variables instead of raw values โœจ

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

which would match:

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

The --enable-var-scope option has the same effect on numeric variables as it does on string variables ๐Ÿฆ‹

Important note: In its current implementation, an expression cannot use a numeric variable defined earlier in the same CHECK directive. Be careful with this one, bestie ๐Ÿ˜ญ

โœจ FileCheck Pseudo Numeric Variables โœจ

Sometimes you need to verify output that contains line numbers from the match file, like when you're testing compiler diagnostics. The problem is that this makes your match file kinda fragile, since CHECK: lines hold absolute line numbers in the same file, and those have to get updated every time line numbers shift because of text getting added or deleted ๐Ÿฅฒ

To fix that, FileCheck expressions understand the @LINE pseudo numeric variable. It evaluates to the line number of the CHECK pattern where it's found ๐ŸŒธ

So you can park your match patterns right next to the relevant test lines and use relative line number references, like this:

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

For the legacy crowd who still use @LINE as a special string variable, FileCheck also accepts these forms with the string substitution block syntax. No spaces inside the brackets, and the offset has to be an integer ๐Ÿ‘‘

  • [[@LINE]] โ€” the current line number, no offset
  • [[@LINE+<offset>]] โ€” the current line number plus an offset
  • [[@LINE-<offset>]] โ€” the current line number minus an offset

โœจ Matching Newline Characters โœจ

If you want to match newline characters in regular expressions, use the character class [[:space:]] ๐Ÿงš For example, this pattern:

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

matches output of this form (from llvm-dwarfdump):

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

This lets us set the FileCheck variable DLOC to the desired value 0x00000233, pulled from the line right before "intd". Main character energy, no cap ๐Ÿฆ‹

โœจ AUTHOR โœจ

Maintained by the LLVM Team (https://llvm.org/) ๐Ÿซถ

2003-2024, LLVM Project ๐ŸŒˆ


And that's the whole page, bestie ๐ŸŽ€ This one's version 15, dated 2024-04-14. Stay iconic, and go slay those match files ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from