FILECHECK(1)

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

Okay bestie, this page lives in the LLVM manual, and it's all about FileCheck ๐Ÿ’–


โœจ NAME โœจ

FileCheck - Flexible pattern matching file verifier ๐Ÿฆ‹

๐Ÿงš SYNOPSIS ๐Ÿงš

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

๐Ÿ’… DESCRIPTION ๐Ÿ’…

Okay so FileCheck reads two files: one comes in through standard input, and one you name on the command line. Then it uses one to verify the other ๐Ÿ”ฅ This is literally a lifesaver for the testsuite, because it checks that a tool's output (like llc) has the info you expect. Think a movsd from esp or whatever is giving you main character energy in the output. It's kind of like grep, but it's built for matching multiple different patterns inside one file, in a specific order. Iconic. ๐Ÿ‘‘

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 ๐ŸŒธ

Okay, options can come from the environment variable FILECHECK_OPTS and from the command line, so you've got options (pun intended) ๐Ÿ’…

  • -help โ€” Prints a summary of the command line options. Your go-to when you forget what's what ๐Ÿ™ˆ
  • --check-prefix prefix โ€” By default, FileCheck goes hunting through match-filename for patterns marked with "CHECK:". If that prefix doesn't fit your vibe (maybe the same input file is checking a bunch of different tools or options), you can swap it out with this flag. Just leave off the trailing ":", and you can pass in one or more prefixes. Multiple prefixes are super handy for tests that change between run options but mostly stay the same ๐Ÿฆ‹

Bestie, FileCheck does not allow duplicate prefixes, even if one is a check prefix and another is a comment prefix (see --comment-prefixes below). No copycats allowed ๐Ÿ’€

  • --check-prefixes prefix1,prefix2,... โ€” An alias of --check-prefix that lets you pass multiple prefixes as a comma separated list. Same energy, just tidier ๐ŸŽ€
  • --comment-prefixes prefix1,prefix2,... โ€” By default, FileCheck ignores any check prefix showing up in match-filename if the same line already has "COM:" or "RUN:" in front of it. Check out the section The "COM:" directive for the deets ๐Ÿ“

You can override these default comment prefixes with --comment-prefixes if they're not working for your testing environment. Lowkey though, it's not recommended in LLVM's LIT-based test suites, because those are way easier to maintain when everyone follows the same comment style. If the defaults aren't cutting it, consider 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 will throw an error and list the missing prefixes. The default is false ๐Ÿ’–
  • --input-file filename โ€” The file to check. If you don't use this, it defaults to stdin ๐Ÿงš
  • --match-full-lines โ€” By default, FileCheck is chill and lets matches happen anywhere on a line. This option is stricter: every positive match has to cover an entire line. Leading and trailing whitespace gets ignored, unless you also pass --strict-whitespace.

Heads up, though: negative matches from CHECK-NOT are not affected by this option! ๐Ÿ™ˆ

Passing this option is the same as putting {{^ *}} or {{^}} before, and {{ *$}} or {{$}} after, every positive check pattern:

{{^ *}}   and   {{ *$}}
{{^}}     and   {{$}}
  • --strict-whitespace โ€” By default, FileCheck canonicalizes horizontal whitespace in the input (spaces and tabs), so it ignores those differences. That means a space will match a tab, which is kinda slay. This flag turns that off. End-of-line sequences get canonicalized to UNIX-style \n in all modes, though ๐ŸŒˆ
  • --ignore-case โ€” By default, FileCheck uses case-sensitive matching. This option switches it to case-insensitive matching, so CHECK and check are both fair game ๐Ÿฆ‹
  • --implicit-check-not check-pattern โ€” This adds implicit negative checks for the patterns you specify, in between the positive checks. It lets you write stricter tests without stuffing them full of CHECK-NOTs. It's so efficient ๐Ÿ‘‘

For example, --implicit-check-not warning: can be super useful when you're testing diagnostic messages from tools that don't have an option like clang's -verify. With this option, FileCheck will verify that the input doesn't contain any warnings that aren't covered by a CHECK: pattern. Not a single rogue warning gets through ๐Ÿ’…

  • --dump-input <value> โ€” Dumps the input to stderr and adds annotations for the diagnostics that are currently enabled. If you use this option more than once, 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, this prints <N> input lines before and <N> input lines after any lines picked out by --dump-input-filter. If you use this option multiple times, the largest <N> wins. The default is 5 ๐ŸŒธ
  • --dump-input-filter <value> โ€” In the dump requested by --dump-input, this prints only the input lines of the kind <value>, plus whatever context --dump-input-context asks for. If you use this option more than once, 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 ๐Ÿ’€

Okay so, in this case we're testing that we get the expected code generation with both 32-bit and 64-bit code generation โœจ

๐Ÿ’– The "COM:" directive ๐Ÿ’–

Sometimes you wanna turn off a FileCheck directive without deleting it entirely, or you want to write comments that mention a directive by name. That's where the COM: directive comes in and makes it super 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 need some combo of rewording and directive syntax mangling to stop FileCheck from recognizing the commented occurrences of X32: and X64: above as directives. Plus, FileCheck diagnostics have been proposed that might complain about those X64 occurrences that don't have the trailing :, since they look like directive typos ๐Ÿ™ˆ Dodging all of this is tedious for a test author, and directive syntax mangling can make the purpose of the test code unclear. COM: avoids all these problems, periodt ๐Ÿ’…

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 as any other directive, so only the first directive on the line is recognized as a directive.

  • RUN: is a comment too โ€” For the sake of LIT, FileCheck treats RUN: just like COM:. If that's not suitable for your test environment, see --comment-prefixes ๐ŸŒธ
  • Suffix combos don't count โ€” 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, such as -NEXT: or -NOT: (discussed below). FileCheck treats such a combination as plain text instead. If it needs to act as a comment directive for your test environment, define it as one with --comment-prefixes โœจ

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

Sometimes you wanna match lines and verify that the matches happen on exactly consecutive lines, with no other lines in between. For that, use CHECK: and CHECK-NEXT: together. If you specified a custom check prefix, just use <PREFIX>-NEXT:. 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 ๐Ÿ™€

๐ŸŒˆ The "CHECK-SAME:" directive ๐ŸŒˆ

Sometimes you want matches to happen on the same line as the previous match. Use CHECK: and CHECK-SAME: for that. If you specified a custom check prefix, just use <PREFIX>-SAME:.

CHECK-SAME: is especially powerful when paired with CHECK-NOT: (described below). For example, this 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 handy for skipping matchers on irrelevant fields. 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 verifies foo has the value 1, you might first write this:

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

Ngl, this would be a bad test ๐Ÿ’€ If the value for foo changes, the test would still pass, because the CHECK: Value: 1 line would match the value from baz. You could fix it by adding CHECK-NEXT matchers for every FieldN: line, but that's verbose and would need updating whenever Field4 is added. A more succinct way, 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 nothing on it, 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 directive will fail if there is more than one newline before it finds the next blank line, and it cannot be the first directive in a file.

๐Ÿ“ The "CHECK-NOT:" directive ๐Ÿ“

The CHECK-NOT: directive verifies that a string doesn't occur 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, a test like this can be used:

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 repeat a plain CHECK: as many times as needed. If that looks too boring, you can instead use a counted check, CHECK-COUNT-<num>:, where <num> is a positive decimal number. It will match the pattern exactly <num> times, no more and no less. If you specified a custom check prefix, just 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 it's necessary to match strings that don't occur 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. Using 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, the 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 running from the definition of a variable to its use. It's useful, e.g., 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 are 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 โš ๏ธ In the case of 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. Not only is this non-overlapping behavior consistent with other directives, 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, 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 so, picture this ๐Ÿซ  you've got a test file split into logical blocks, and a CHECK: line from one block sneaks into a later block and "passes" ๐Ÿ’€ ngl an error usually shows up eventually, but it's pointing at a check that has nothing to do with the actual problem. Not it.

That's where CHECK-LABEL: comes in ๐Ÿฆธโ€โ™€๏ธ It works exactly like a normal CHECK directive, EXCEPT FileCheck assumes a line matched by it can't also be matched by any other check in match-filename. It's basically made for labels or other unique identifiers ๐Ÿท๏ธ Conceptually, it chops the input stream into separate blocks, each processed on its own, 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 wiped at the start of each block ๐Ÿงน

Here's an example bestie:

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: lines here make sure the three CHECK: directives only accept lines from the body of the @C_ctor_base function ๐ŸŽฏ even if their patterns match stuff way further down the file. And if one of those three CHECK: directives fails, FileCheck just keeps going to the next block, so you can catch multiple failures in a single invocation ๐Ÿ™Œ

There's no rule that CHECK-LABEL: strings have to match real 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.

โœจ Directive modifiers โœจ

You can tack a directive modifier onto a directive by following it with {<modifier>} ๐Ÿ’… The only supported value for <modifier> is LITERAL.

The LITERAL modifier does a literal match ๐Ÿ“ It turns off regex matching, variable capture, and substitutions, which is perfect when the text you're matching would need excessive escaping otherwise. For example, these will be treated as literal matches instead of 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 things, fixed string matching is perfectly sufficient. When you need something more flexible, you can write a regular expression wrapped in double braces: {{yourregex}}. FileCheck uses a POSIX regular expression matcher and supports Extended POSIX regular expressions (ERE) ๐Ÿงš Since most of what we do is fixed string matching, FileCheck lets you 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 ๐Ÿ’œ

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 want to match double braces literally 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 {{([[: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 check that it shows up again later in the file ๐Ÿ‘‘ For codegen tests, this lets you accept any register but verify that the same register gets used consistently later. FileCheck supports string substitution blocks for this, letting 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 useful if you want both operands of op to be the same register, and you don't care exactly which register it is ๐Ÿซถ

If --enable-var-scope is in effect, variables whose names start with $ are considered global, and all others 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 ๐Ÿ”ข These let you define numeric variables and check for numeric values that satisfy a numeric expression constraint based on those variables. This means CHECK: directives can verify a numeric relation between two numbers, like making sure consecutive registers are used ๐ŸŒˆ

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

  • %<fmtspec>, is an optional format specifier that says what 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, with leading zeros expected if needed.
  • <conversion specifier> is an optional scanf-style conversion specifier that says what number format to match (e.g. hex number). 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 can be handy when the value itself isn't useful, for instance:

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

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 as for defining a variable, but in this context it indicates how a numeric expression value should be matched. If it's absent, both components 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 have conflicting 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 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 is a predefined string literal. 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]]

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

Why does that one bonk? Because 7 is unequal to 5 + 1, and a0463443 is unequal to a0463440 + 7. ๐Ÿ’€

Okay so, 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 is 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 ๐Ÿ’…. Bestie, you can use this to make a testcase way more self-describing by using 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]]]

And it would totally match this ๐Ÿฆ‹:

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. Don't even try it, bestie ๐Ÿ’€

โœจ FileCheck Pseudo Numeric Variables โœจ

Sometimes you need to verify output that contains line numbers of the match file, like when you're testing compiler diagnostics ๐Ÿง. It's lowkey fragile though, because CHECK: lines contain absolute line numbers in the same file, and those have to be updated every single time line numbers shift from text being added or deleted ๐Ÿ˜ฉ.

To help with this, FileCheck expressions understand the @LINE pseudo numeric variable ๐ŸŒธ, which evaluates to the line number of the CHECK pattern where it's found. Slay.

This way, match patterns can live right near the relevant test lines and use relative line number references, like so:

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

To keep legacy uses of @LINE working as a special string variable, FileCheck also accepts these forms with string substitution block syntax: [[@LINE]], [[@LINE+<offset>]] and [[@LINE-<offset>]]. Just make sure there are no spaces inside the brackets, and offset has to be an integer ๐Ÿงš.

โœจ Matching Newline Characters โœจ

Okay so, 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 the form (from llvm-dwarfdump):

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

which lets us set the FileCheck variable DLOC to the desired value 0x00000233, pulled from the line immediately preceding "intd" ๐Ÿ‘‘. That's giving precision.


โœจ AUTHOR โœจ

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

2003-2026, LLVM Project ๐Ÿ’œ

Bestie, that's the whole page ๐ŸŽ€ This one's from source/version 22, dated 2026-07-16, so go forth and check ur FileCheck files ๐Ÿฆ‹๐Ÿ’–

๐Ÿ“ฆ where this page comes from