FILECHECK(1)

โœจ FILECHECK(1) from LLVM โœจ


๐ŸŽ€ NAME ๐ŸŽ€

FileCheck is 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 name on the command line, and then it uses one of them to verify the other ๐Ÿซถ this is literally the move for the testsuite, which needs to confirm that some tool's output (like llc) has the info it's supposed to, like a movsd from esp or whatever is interesting ๐Ÿ‘€ it's kinda like grep, but it's built for matching a bunch of different things inside one file, in a specific order โœจ

The match-filename file is the one holding the patterns to match. The file being verified 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 searches match-filename for patterns to match, and by default they're prefixed with CHECK: ๐Ÿ” if you want a different prefix (maybe the same input file is checking multiple tools or options), this lets you pick one or more prefixes to match, without the trailing :. multiple prefixes are super useful for tests that change between run options but where most lines stay the same ๐ŸŒˆ FileCheck does not 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 multiple prefixes, comma separated ๐Ÿ“
  • --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: ๐Ÿ™ˆ see the section The "COM:" directive for usage details. you can override these default comment prefixes with --comment-prefixes if they don't fit your testing environment, but it's not recommended in LLVM's LIT-based test suites, which are way easier to maintain when everything follows one consistent comment style. if that's your situation, consider proposing a change to the default comment prefixes instead ๐Ÿ’ญ
  • --allow-unused-prefixes โ€” this controls what happens when you use more than one prefix (through --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 reports an error listing the missing prefixes. the default value is false ๐Ÿšซ
  • --input-file filename โ€” the file to check (defaults to stdin) ๐Ÿ“ฅ
  • --match-full-lines โ€” by default, matches can happen anywhere on a line, but this option makes every positive match cover an entire line ๐Ÿ“ leading and trailing whitespace gets ignored, unless --strict-whitespace is also specified. (Note: negative matches from CHECK-NOT are not affected by this option!) passing this option is the same as inserting {{^ *}} or {{^}} before, and {{ *$}} or {{$}} after, every positive check pattern ๐Ÿง
  • --strict-whitespace โ€” by default, FileCheck canonicalizes horizontal whitespace (spaces and tabs), so a space will match a tab ๐Ÿซ  this disables that behavior. end-of-line sequences get canonicalized to UNIX-style \n in all modes, though ๐Ÿง
  • --ignore-case โ€” by default, matching is case-sensitive. this makes FileCheck use case-insensitive matching instead ๐Ÿ”ค
  • --implicit-check-not check-pattern โ€” adds implicit negative checks for the patterns you specify, between positive checks. this lets you write stricter tests without stuffing them full of CHECK-NOTs ๐Ÿง  for example, --implicit-check-not warning: can be really useful when 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, adding annotations for the diagnostics that are currently enabled ๐Ÿ—‚๏ธ when this option shows up multiple times, the <value> that appears 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 requested by --dump-input, prints <N> input lines before and <N> input lines after any lines specified by --dump-input-filter ๐Ÿงพ when this option shows up multiple times, the largest specified <N> wins. the default is 5 ๐Ÿ”ข
  • --dump-input-filter <value> โ€” in the --dump-input dump, prints only input lines of kind <value>, plus any context from --dump-input-context ๐ŸŽฏ when this option shows up multiple times, the <value> that appears earliest in the list below wins. the default is error when --dump-input=fail, and it's 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 โ€” enables scope for regex variables ๐Ÿช„ variables whose names start with $ are considered global and stay set throughout the whole file. all other variables get undefined after each CHECK-LABEL that's encountered ๐Ÿงน
  • -D<VAR=VALUE> โ€” sets a FileCheck pattern variable VAR with the value VALUE, which can be used 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 can be used in CHECK: lines ๐Ÿ”ขโœจ see section FileCheck Numeric Variables and Expressions for details on the supported numeric expressions ๐Ÿ“–
  • -version โ€” shows the version number of this program ๐Ÿท๏ธ
  • -v โ€” prints good directive pattern matches ๐Ÿ‘€ however, if -dump-input=fail or -dump-input=always is set, those matches get added as input annotations instead ๐Ÿ–๏ธ
  • -vv โ€” prints info that's helpful for diagnosing internal FileCheck issues, like discarded overlapping CHECK-DAG: matches, implicit EOF pattern matches, and CHECK-NOT: patterns that don't have any matches ๐Ÿ•ต๏ธโ€โ™€๏ธ implies -v. however, if -dump-input=fail or -dump-input=always is set, that info just 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 provided 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 is rejected ๐Ÿซ™๐Ÿšซ
  • --color โ€” uses colors in the output ๐ŸŒˆ (autodetected by default) ๐ŸŽจ

In this case, we're making sure we get the expected code generation for both 32-bit and 64-bit targets, bestie ๐Ÿ’…๐ŸŽ€

๐Ÿ’ฌ The COM: directive ๐Ÿ’ฌ

Sometimes you want to switch off a FileCheck directive without deleting it entirely, or you want to write comments that name a directive out loud. 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 clock the commented X32: and X64: lines above as real directives ๐Ÿ˜ญ And FileCheck diagnostics have been proposed that might yell about those X64 mentions that don't have the trailing :, because they look like directive typos. Dodging all of that is exhausting for a test author, and mangled syntax can make the point of the test code super unclear. COM: skips all of that drama โœจ

A few important usage notes:

  • COM: inside a pattern: COM: within 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 to another line ๐Ÿ™ˆ The reason is that FileCheck parses COM: the same way it parses any other directive, so only the first directive on the line gets recognized.

  • RUN: gets the same treatment: For the sake of LIT, FileCheck treats RUN: just like COM:. If that doesn't vibe with your test environment, check out --comment-prefixes.
  • Suffix combos are not it: 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). FileCheck treats that combo as plain text instead ๐Ÿฅด If it needs to act 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. You can use CHECK: and CHECK-NEXT: to pull that off ๐Ÿ’– If you set a custom check prefix, just use <PREFIX>-NEXT: instead. For example, 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
}
CHECK-NEXT: directives reject the input unless there is exactly one newline between them and the previous directive. A CHECK-NEXT: also cannot be the first directive in a file, so don't put it at the top ๐Ÿšซ

๐Ÿ“ The CHECK-SAME: directive ๐Ÿ“

Sometimes you want to match lines and verify that a match happens on the same line as the previous match. Use CHECK: and CHECK-SAME: for that ๐Ÿฆ‹ With a custom check prefix, it's <PREFIX>-SAME:.

CHECK-SAME: is seriously powerful when you pair it with CHECK-NOT: (described below) ๐Ÿ”ฅ

For example, the following works like you'd expect:

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

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

CHECK-SAME: directives reject the input if there are any newlines between them and the previous directive.

CHECK-SAME: is also super useful for skipping matchers for stuff you don't care about. Say you're writing a test that parses a tool generating 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 write a test that checks foo has the value 1, you might first write this:

CHECK: Name: foo
CHECK: Value: 1{{$}}
Hot take: this would be a bad test ๐Ÿ’€ If foo's value changes, the test would still pass, because the CHECK: Value: 1 line would match the value from baz. You could add CHECK-NEXT matchers for every FieldN: line to fix it, but that's verbose and would need updating every time Field4 shows up. A more succinct way to write it, using the CHECK-SAME: matcher, looks like this:
CHECK:      Name: foo
CHECK:      Value:
CHECK-SAME:        {{ 1$}}

This verifies that the next time Value: appears 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 has absolutely nothing on it, not even whitespace, the CHECK-EMPTY: directive is your girl ๐Ÿงš

declare void @foo()

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

Just like CHECK-NEXT:, this directive will fail if there's more than one newline before it finds the next blank line. It also cannot be the first directive in a file.


๐Ÿšซ The CHECK-NOT: directive ๐Ÿšซ

The CHECK-NOT: directive is used to verify that a string doesn't show up between two matches (or before the first match, or after the last match) ๐Ÿ‘€ For example, to verify that a load gets removed by a transformation, you can use a test like this:

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 multiple lines with the same pattern over and over, you can just repeat a plain CHECK: as many times as you need. If that feels too boring, you can use a counted check instead: CHECK-COUNT-<num>:, where <num> is a positive decimal number ๐Ÿ’… It will match 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 ๐Ÿงฉ

If you need to match strings that don't show up in a strictly sequential order, CHECK-DAG: can verify them between two matches (or before the first match, or after the last match) ๐ŸŒˆ For example, clang emits vtable globals in reverse order. With CHECK-DAG:, we can keep the 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 with CHECK-DAG: directives to exclude strings between the surrounding CHECK-DAG: directives ๐Ÿ™…โ€โ™€๏ธ As a result, those surrounding CHECK-DAG: directives cannot be reordered. In other words, all occurrences matching CHECK-DAG: before CHECK-NOT: must not fall behind occurrences matching 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 ๐Ÿง  It's useful when your test cases 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]

While 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]
While this can be very useful, it's also dangerous ๐Ÿ˜ญ With register sequences, you must have a strong order (read before write, copy before use, etc). If the definition your test is looking for doesn't match (because of a bug in the compiler), it may match further away from the use and mask real bugs. In those cases, to enforce the order, use a non-DAG directive between 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 to handle sets of non-unique strings or patterns ๐Ÿซถ For example, the following directives look for unordered log entries for two tasks in a parallel program, such as 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. That's true even though the patterns are identical, and even if the text of the log entries is identical, because the thread ID manages to be reused ๐Ÿงšโ€โ™€๏ธ

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

okay bestie, so sometimes you've got a file full of tests split into logical blocks, and one or more CHECK: directives go "oh I got this!" and succeed by matching lines in a later block ๐Ÿ˜ญ. An error usually shows up eventually, but the check it blames might have zero connection to the real problem. not the vibe ๐Ÿ™…โ€โ™€๏ธ

that's where CHECK-LABEL: comes in ๐Ÿ‘‘. It works just like a normal CHECK directive, except FileCheck makes one extra assumption: a line matched by a CHECK-LABEL: can't also be matched by any other check in match-filename. it's meant for lines with labels or other unique identifiers ๐ŸŽ€. basically, a CHECK-LABEL: splits the input stream into separate blocks, and each block gets processed on its own, so a CHECK: in one block can't steal a line from another block ๐Ÿงš. and if --enable-var-scope is in effect, all local variables get wiped at the start 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:

the CHECK-LABEL: directives here make sure the three CHECK: directives only accept lines from the body of the @C_ctor_base function, even if the patterns match lines later in the file. the best part? if one of those three CHECK: directives fails, FileCheck just keeps going to the next block ๐Ÿ’…. so you can catch multiple test failures in a single invocation. literally so useful ๐Ÿ˜ญโœจ

there's no rule that CHECK-LABEL: strings have to match real syntactic labels in a source or output language ๐ŸŒˆ. they just need to uniquely match a single line in the file being verified.

โš ๏ธ CHECK-LABEL: directives cannot contain variable definitions or uses. no exceptions, bestie ๐Ÿ’€

โœจ Directive modifiers โœจ

okay so you can slap a directive modifier onto a directive by following it with {<modifier>}, and the only supported value for <modifier> is LITERAL ๐Ÿ“

the LITERAL modifier does a literal match. it turns off any regex syntax, variable capture, or substitutions, which is super handy when the text you're matching would need a ton of escaping otherwise ๐Ÿฆ‹. for example, these lines do literal matches instead of being treated as regexes:

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 stuff, fixed string matching is totally enough. but for the fancier stuff, you can write regexes wrapped in double braces: {{yourregex}}. FileCheck implements a POSIX regex matcher and supports Extended POSIX regular expressions (ERE) ๐Ÿง . since fixed strings do most of the heavy lifting, FileCheck is built to mix and match them with regexes, so you can write things like this:

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

here, any offset from the ESP register is allowed, and any xmm register is allowed ๐Ÿ’–

because regexes are wrapped in double braces, they're visually distinct, and you don't need escape characters inside them like you would in C ๐Ÿ™Œ. in the rare case you need 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'd need to add parentheses like {{([[:xdigit:]]{8})}} so it doesn't get confused with FileCheck's closing double-brace ๐Ÿซถ


โœจ FileCheck String Substitution Blocks โœจ

it's often super useful to match a pattern and then verify it shows up again later in the file ๐Ÿ‘€. for codegen tests, this lets you allow any register but still check that the same register gets used consistently later. that's what string substitution blocks are for: you can define string variables and substitute them into patterns ๐Ÿ”ฅ. here's a simple one:

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

the first check matches the regex %[a-z]+ and captures it into the string variable REGISTER. the second line verifies that whatever's in REGISTER shows up later in the file after an "andw" ๐Ÿ’…. string substitution blocks are always wrapped in [[ ]] pairs, and 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 โœจ

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 useful if you want the operands of op to be the same register and don't care exactly which one 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 has numeric substitution blocks ๐Ÿ”ข. you can define numeric variables and check for numeric values that satisfy a numeric expression constraint based on those variables. this lets CHECK: directives verify a numeric relation between two numbers, like needing consecutive registers to be used ๐ŸŒˆ

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

  • %<fmtspec>, โ€” an optional format specifier that says what number format to match, plus the minimum number of digits to expect.
  • <NUMVAR>: โ€” an optional definition of the variable <NUMVAR> from the captured value.

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

  • # โ€” an optional flag for hex values (see <conversion specifier> below) that requires the matched value to be prefixed by 0x.
  • .<precision> โ€” 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> โ€” an optional scanf-style conversion specifier that says what number format to match (e.g. hex). right now the accepted ones are %u, %d, %x and %X. if it's absent, it 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. but 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 doesn't matter ๐Ÿ’œ

; 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> โ€” the same format specifier you'd use when defining a variable, but here it's about how a numeric expression's value should be matched against. if it's absent, both components get inferred from the matching format of whatever numeric variable(s) the expression constraint uses. if no numeric variable is used, it defaults to %u, meaning unsigned with no leading zeros. if several numeric variables conflict on their 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 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> โ€” an expression. it's 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 you can use parentheses 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> โ€” 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> โ€” 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]]

the above example would match the text:

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

but would not match the text:

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

๐Ÿ™ˆ So it fails bestie because 7 is unequal to 5 + 1, and a0463443 is unequal to a0463440 + 7.

A numeric variable can also be set to the result of a numeric expression. When that happens, the numeric expression constraint gets checked, and if it passes, the variable gets assigned that value. The unified syntax for checking a numeric expression and capturing its value into a numeric variable is [[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]], with each element working the way we covered earlier. This is lowkey the best way to make a testcase self-describing, because you 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]]]

That would match:

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

The --enable-var-scope option does the same thing to numeric variables that it does to string variables ๐Ÿฆ‹

๐Ÿ’€ Important note: in its current implementation, an expression cannot use a numeric variable defined earlier in the same CHECK directive. Not it, not allowed, don't try it ๐Ÿ˜ญ

๐ŸŒธ FileCheck Pseudo Numeric Variables ๐ŸŒธ

Okay so sometimes you need to verify output that includes line numbers from the match file, like when you're testing compiler diagnostics. This gets a little fragile, because 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. Literally so annoying ๐Ÿ’…

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

That means your match patterns can live 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

Also, for legacy code that treats @LINE as a special string variable, FileCheck still accepts these forms with the string substitution block syntax: [[@LINE]], [[@LINE+<offset>]] and [[@LINE-<offset>]]. There must be no spaces inside the brackets, and offset has to be an integer ๐ŸŽ€


๐Ÿฆ‹ Matching Newline Characters ๐Ÿฆ‹

To match newline characters in regular expressions, you can 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")

That lets us set the FileCheck variable DLOC to the value 0x00000233, pulled from the line right before "intd" ๐Ÿ’œ


โœจ AUTHOR โœจ

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

2003-2025, LLVM Project ๐ŸŒˆ


And that's the whole FILECHECK(1) page, bestie ๐Ÿ’– Version 20, last updated 2025-09-03, and it's giving main character energy the whole way through ๐Ÿ”ฅ๐Ÿ’…

๐Ÿ“ฆ where this page comes from