blob: b04b0d82535f88bc2e08c4d09bcd370ca58d024c [file] [log] [blame]
David Brazdil2c27f2c2015-05-12 18:06:38 +01001Checker is a testing tool which compiles a given test file and compares the
2state of the control-flow graph before and after each optimization pass
David Brazdil9bbed3c2016-05-18 12:35:11 +01003against a set of statements specified alongside the tests.
David Brazdil2c27f2c2015-05-12 18:06:38 +01004
David Brazdilb34c35e2015-08-20 11:46:04 +01005Tests are written in Java or Smali, turned into DEX and compiled with the
David Brazdil9bbed3c2016-05-18 12:35:11 +01006Optimizing compiler. "Check lines" are statements formatted as comments of the
David Brazdilb34c35e2015-08-20 11:46:04 +01007source file. They begin with prefix "/// CHECK" or "## CHECK", respectively,
8followed by a pattern that the engine attempts to match in the compiler output.
David Brazdil2c27f2c2015-05-12 18:06:38 +01009
David Brazdil9bbed3c2016-05-18 12:35:11 +010010Statements are tested in groups which correspond to the individual compiler
David Brazdil2c27f2c2015-05-12 18:06:38 +010011passes. Each group of check lines therefore must start with a 'CHECK-START'
12header which specifies the output group it should be tested against. The group
13name must exactly match one of the groups recognized in the output (they can
David Brazdilc2c48ff2015-05-15 14:24:31 +010014be listed with the '--list-passes' command-line flag).
David Brazdil2c27f2c2015-05-12 18:06:38 +010015
16Matching of check lines is carried out in the order of appearance in the
David Brazdil06d98542016-05-20 16:34:27 +010017source file. There are five types of check lines. Branching instructions are
18also supported and documented later in this file.
David Brazdilb34c35e2015-08-20 11:46:04 +010019 - CHECK: Must match an output line which appears in the output group
20 later than lines matched against any preceeding checks. Output
21 lines must therefore match the check lines in the same order.
22 These are referred to as "in-order" checks in the code.
23 - CHECK-DAG: Must match an output line which appears in the output group
24 later than lines matched against any preceeding in-order checks.
25 In other words, the order of output lines does not matter
26 between consecutive DAG checks.
27 - CHECK-NOT: Must not match any output line which appears in the output group
28 later than lines matched against any preceeding checks and
29 earlier than lines matched against any subsequent checks.
30 Surrounding non-negative checks (or boundaries of the group)
David Brazdil9bbed3c2016-05-18 12:35:11 +010031 therefore create a scope within which the statement is verified.
David Brazdilb34c35e2015-08-20 11:46:04 +010032 - CHECK-NEXT: Must match the output line which comes right after the line which
David Brazdil8b934b12016-05-19 15:39:25 +010033 matched the previous check. Can only be used after a CHECK or
34 another CHECK-NEXT.
David Brazdilb34c35e2015-08-20 11:46:04 +010035 - CHECK-EVAL: Specifies a Python expression which must evaluate to 'True'.
David Brazdil2c27f2c2015-05-12 18:06:38 +010036
37Check-line patterns are treated as plain text rather than regular expressions
38but are whitespace agnostic.
39
40Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
41curly brackets need to be used inside the body of the regex, they need to be
42enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse
43the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'.
44
45Regex patterns can be named and referenced later. A new variable is defined
David Brazdilc2c48ff2015-05-15 14:24:31 +010046with '<<name:regex>>' and can be referenced with '<<name>>'. Variables are
David Brazdil2c27f2c2015-05-12 18:06:38 +010047only valid within the scope of the defining group. Within a group they cannot
48be redefined or used undefined.
49
50Example:
David Brazdil9bbed3c2016-05-18 12:35:11 +010051 The following statements can be placed in a Java source file:
David Brazdil2c27f2c2015-05-12 18:06:38 +010052
David Brazdilb34c35e2015-08-20 11:46:04 +010053 /// CHECK-START: int MyClass.MyMethod() constant_folding (after)
54 /// CHECK: <<ID:i\d+>> IntConstant {{11|22}}
55 /// CHECK: Return [<<ID>>]
David Brazdil2c27f2c2015-05-12 18:06:38 +010056
57 The engine will attempt to match the check lines against the output of the
58 group named on the first line. Together they verify that the CFG after
59 constant folding returns an integer constant with value either 11 or 22.
Alexandre Rames5e2c8d32015-08-06 14:49:28 +010060
David Brazdilb34c35e2015-08-20 11:46:04 +010061
62Of the language constructs above, 'CHECK-EVAL' lines support only referencing of
63variables. Any other surrounding text will be passed to Python's `eval` as is.
64
65Example:
66 /// CHECK-START: int MyClass.MyMethod() liveness (after)
67 /// CHECK: InstructionA liveness:<<VarA:\d+>>
68 /// CHECK: InstructionB liveness:<<VarB:\d+>>
69 /// CHECK-EVAL: <<VarA>> != <<VarB>>
70
71
Alexandre Rames5e2c8d32015-08-06 14:49:28 +010072A group of check lines can be made architecture-specific by inserting '-<arch>'
73after the 'CHECK-START' keyword. The previous example can be updated to run for
74arm64 only with:
75
David Brazdilb34c35e2015-08-20 11:46:04 +010076Example:
77 /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after)
78 /// CHECK: <<ID:i\d+>> IntConstant {{11|22}}
79 /// CHECK: Return [<<ID>>]
Aart Bik92706a82017-11-30 11:46:45 -080080
81For convenience, several architectures can be specified as set after the
82'CHECK-START' keyword. Any listed architecture will match in that case,
83thereby avoiding to repeat the check lines if some, but not all architectures
84match. An example line looks like:
85
Vladimir Markobe0d3cf2020-02-12 10:52:22 +000086 /// CHECK-START-{X86_64,ARM,ARM64}: int MyClass.MyMethod() constant_folding (after)
David Brazdil06d98542016-05-20 16:34:27 +010087
88
89Branching is possible thanks to the following statements:
90 - CHECK-IF:
91 - CHECK-ELIF:
92 - CHECK-ELSE:
93 - CHECK-FI:
94
95CHECK-IF and CHECK-ELIF take a Python expression as input that will be evaluated by `eval`.
Fabio Rinaldi40b06142020-02-12 16:18:50 +000096
97A possible use case of branching is to check whether the generated code exploits the instruction
98architecture features enabled at compile time. For that purpose, you can call the custom made
99function isaHasFeature("feature_name").
100
101Example:
102 /// CHECK-START-ARM64: int other.TestByte.testDotProdComplex(byte[], byte[]) disassembly (after)
103 /// CHECK: VecDotProd
104 /// CHECK-IF: isaHasFeature("dotprod")
105 /// CHECK: sdot
106 /// CHECK-ELSE:
107 /// CHECK-NOT: sdot
108 /// CHECK-FI:
109
110Like CHECK-EVAL, CHECK-IF and CHECK-ELIF support only referencing of variables, defining new
111variables as part of the statement input is not allowed. Any other surrounding text will be passed
112to Python's `eval` as is. CHECK-ELSE and CHECK-FI must not have any input.
David Brazdil06d98542016-05-20 16:34:27 +0100113
114Example:
115 /// CHECK-START: int MyClass.MyMethod() constant_folding (after)
116 /// CHECK: {{i\d+}} IntConstant <<MyConst:(0|1|2)>>
117 /// CHECK-IF: <<MyConst>> == 0
118 /// CHECK-NEXT: FooBar01
119 /// CHECK-ELIF: <<MyConst>> == 1
120 /// CHECK-NOT: FooBar01
121 /// CHECK-FI:
122
123Branch blocks can contain any statement, including CHECK-NEXT and CHECK-DAG.
124Notice the CHECK-NEXT statement within the IF branch. When a CHECK-NEXT is encountered,
125Checker expects that the previously executed statement was either a CHECK or a CHECK-NEXT.
126This condition is enforced at runtime, and an error is thrown if it's not respected.
127
128Statements inside branches can define new variables. If a new variable gets defined inside a branch
129(of any depth, since nested branching is allowed), that variable will become global within the scope
130of the defining group. In other words, it will be valid everywhere after its definition within the
131block defined by the CHECK-START statement. The absence of lexical scoping for Checker variables
132seems a bit inelegant at first, but is probably more practical.
133
134Example:
135 /// CHECK-START: void MyClass.FooBar() liveness (after)
136 /// CHECK-IF: os.environ.get('ART_READ_BARRIER_TYPE') != 'TABLELOOKUP'
137 /// CHECK: <<MyID:i\d+>> IntConstant 3
138 /// CHECK-ELSE:
139 /// CHECK: <<MyID:i\d+>> IntConstant 5
140 /// CHECK-FI:
141 /// CHECK-NEXT: Return [<<MyID>>]
142
143Notice that the variable MyID remained valid outside the branch where it was defined.
144Furthermore, in this example, the definition of MyID depends on which branch gets selected at
145runtime. Attempting to re-define a variable or referencing an undefined variable is not allowed,
146Checker will throw a runtime error.
147The example above also shows how we can use environment variables to perform custom checks.
148
149It is possible to combine IF, (multiple) ELIF and ELSE statements together. Nested branching is
150also supported.