David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 1 | Checker is a testing tool which compiles a given test file and compares the |
| 2 | state of the control-flow graph before and after each optimization pass |
David Brazdil | 9bbed3c | 2016-05-18 12:35:11 +0100 | [diff] [blame] | 3 | against a set of statements specified alongside the tests. |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 4 | |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 5 | Tests are written in Java or Smali, turned into DEX and compiled with the |
David Brazdil | 9bbed3c | 2016-05-18 12:35:11 +0100 | [diff] [blame] | 6 | Optimizing compiler. "Check lines" are statements formatted as comments of the |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 7 | source file. They begin with prefix "/// CHECK" or "## CHECK", respectively, |
| 8 | followed by a pattern that the engine attempts to match in the compiler output. |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 9 | |
David Brazdil | 9bbed3c | 2016-05-18 12:35:11 +0100 | [diff] [blame] | 10 | Statements are tested in groups which correspond to the individual compiler |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 11 | passes. Each group of check lines therefore must start with a 'CHECK-START' |
| 12 | header which specifies the output group it should be tested against. The group |
| 13 | name must exactly match one of the groups recognized in the output (they can |
David Brazdil | c2c48ff | 2015-05-15 14:24:31 +0100 | [diff] [blame] | 14 | be listed with the '--list-passes' command-line flag). |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 15 | |
| 16 | Matching of check lines is carried out in the order of appearance in the |
David Brazdil | 06d9854 | 2016-05-20 16:34:27 +0100 | [diff] [blame] | 17 | source file. There are five types of check lines. Branching instructions are |
| 18 | also supported and documented later in this file. |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 19 | - 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 Brazdil | 9bbed3c | 2016-05-18 12:35:11 +0100 | [diff] [blame] | 31 | therefore create a scope within which the statement is verified. |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 32 | - CHECK-NEXT: Must match the output line which comes right after the line which |
David Brazdil | 8b934b1 | 2016-05-19 15:39:25 +0100 | [diff] [blame] | 33 | matched the previous check. Can only be used after a CHECK or |
| 34 | another CHECK-NEXT. |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 35 | - CHECK-EVAL: Specifies a Python expression which must evaluate to 'True'. |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 36 | |
| 37 | Check-line patterns are treated as plain text rather than regular expressions |
| 38 | but are whitespace agnostic. |
| 39 | |
| 40 | Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If |
| 41 | curly brackets need to be used inside the body of the regex, they need to be |
| 42 | enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse |
| 43 | the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'. |
| 44 | |
| 45 | Regex patterns can be named and referenced later. A new variable is defined |
David Brazdil | c2c48ff | 2015-05-15 14:24:31 +0100 | [diff] [blame] | 46 | with '<<name:regex>>' and can be referenced with '<<name>>'. Variables are |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 47 | only valid within the scope of the defining group. Within a group they cannot |
| 48 | be redefined or used undefined. |
| 49 | |
| 50 | Example: |
David Brazdil | 9bbed3c | 2016-05-18 12:35:11 +0100 | [diff] [blame] | 51 | The following statements can be placed in a Java source file: |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 52 | |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 53 | /// CHECK-START: int MyClass.MyMethod() constant_folding (after) |
| 54 | /// CHECK: <<ID:i\d+>> IntConstant {{11|22}} |
| 55 | /// CHECK: Return [<<ID>>] |
David Brazdil | 2c27f2c | 2015-05-12 18:06:38 +0100 | [diff] [blame] | 56 | |
| 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 Rames | 5e2c8d3 | 2015-08-06 14:49:28 +0100 | [diff] [blame] | 60 | |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 61 | |
| 62 | Of the language constructs above, 'CHECK-EVAL' lines support only referencing of |
| 63 | variables. Any other surrounding text will be passed to Python's `eval` as is. |
| 64 | |
| 65 | Example: |
| 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 Rames | 5e2c8d3 | 2015-08-06 14:49:28 +0100 | [diff] [blame] | 72 | A group of check lines can be made architecture-specific by inserting '-<arch>' |
| 73 | after the 'CHECK-START' keyword. The previous example can be updated to run for |
| 74 | arm64 only with: |
| 75 | |
David Brazdil | b34c35e | 2015-08-20 11:46:04 +0100 | [diff] [blame] | 76 | Example: |
| 77 | /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) |
| 78 | /// CHECK: <<ID:i\d+>> IntConstant {{11|22}} |
| 79 | /// CHECK: Return [<<ID>>] |
Aart Bik | 92706a8 | 2017-11-30 11:46:45 -0800 | [diff] [blame] | 80 | |
| 81 | For convenience, several architectures can be specified as set after the |
| 82 | 'CHECK-START' keyword. Any listed architecture will match in that case, |
| 83 | thereby avoiding to repeat the check lines if some, but not all architectures |
| 84 | match. An example line looks like: |
| 85 | |
Vladimir Marko | be0d3cf | 2020-02-12 10:52:22 +0000 | [diff] [blame] | 86 | /// CHECK-START-{X86_64,ARM,ARM64}: int MyClass.MyMethod() constant_folding (after) |
David Brazdil | 06d9854 | 2016-05-20 16:34:27 +0100 | [diff] [blame] | 87 | |
| 88 | |
| 89 | Branching is possible thanks to the following statements: |
| 90 | - CHECK-IF: |
| 91 | - CHECK-ELIF: |
| 92 | - CHECK-ELSE: |
| 93 | - CHECK-FI: |
| 94 | |
| 95 | CHECK-IF and CHECK-ELIF take a Python expression as input that will be evaluated by `eval`. |
Fabio Rinaldi | 40b0614 | 2020-02-12 16:18:50 +0000 | [diff] [blame] | 96 | |
| 97 | A possible use case of branching is to check whether the generated code exploits the instruction |
| 98 | architecture features enabled at compile time. For that purpose, you can call the custom made |
| 99 | function isaHasFeature("feature_name"). |
| 100 | |
| 101 | Example: |
| 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 | |
| 110 | Like CHECK-EVAL, CHECK-IF and CHECK-ELIF support only referencing of variables, defining new |
| 111 | variables as part of the statement input is not allowed. Any other surrounding text will be passed |
| 112 | to Python's `eval` as is. CHECK-ELSE and CHECK-FI must not have any input. |
David Brazdil | 06d9854 | 2016-05-20 16:34:27 +0100 | [diff] [blame] | 113 | |
| 114 | Example: |
| 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 | |
| 123 | Branch blocks can contain any statement, including CHECK-NEXT and CHECK-DAG. |
| 124 | Notice the CHECK-NEXT statement within the IF branch. When a CHECK-NEXT is encountered, |
| 125 | Checker expects that the previously executed statement was either a CHECK or a CHECK-NEXT. |
| 126 | This condition is enforced at runtime, and an error is thrown if it's not respected. |
| 127 | |
| 128 | Statements 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 |
| 130 | of the defining group. In other words, it will be valid everywhere after its definition within the |
| 131 | block defined by the CHECK-START statement. The absence of lexical scoping for Checker variables |
| 132 | seems a bit inelegant at first, but is probably more practical. |
| 133 | |
| 134 | Example: |
| 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 | |
| 143 | Notice that the variable MyID remained valid outside the branch where it was defined. |
| 144 | Furthermore, in this example, the definition of MyID depends on which branch gets selected at |
| 145 | runtime. Attempting to re-define a variable or referencing an undefined variable is not allowed, |
| 146 | Checker will throw a runtime error. |
| 147 | The example above also shows how we can use environment variables to perform custom checks. |
| 148 | |
| 149 | It is possible to combine IF, (multiple) ELIF and ELSE statements together. Nested branching is |
| 150 | also supported. |